叙情的な紹介
次のインタビューでもう一度プロトタイプについてたくさんの質問を受けたので、プロトタイピングの複雑さを少し忘れていたことに気づき、知識を更新することにしました。著者のインスピレーション、彼がプロトタイプをどのように「感じている」か、または記事がトピックの別の部分に関するものであり、何が起こっているのかを完全に把握していないかのいずれかで書かれた記事をたくさん見つけました。
ES5の昔から、そして私が聞いたことのないES6でさえ、多くの非自明なことがあることがわかりました。また、ブラウザコンソールの出力が現実に対応していない可能性があることも判明しました。
プロトタイプとは
JSのオブジェクトには、たとえば次のコードのように、独自の継承されたプロパティがあります。
var foo = { bar: 1 };
foo.bar === 1 // true
typeof foo.toString === "function" // true
オブジェクトにfoo
はbar
値を持つ独自のプロパティがありますが1
、などの他のプロパティもありますtoString
。オブジェクトfoo
が新しいプロパティを取得する方法を理解するために、オブジェクトがtoString
何で構成されているかを見てみましょう。
重要なのは、オブジェクトが別のプロトタイプオブジェクトへの参照を持っているということです。フィールドfoo.toString
にアクセスするとき、そのようなプロパティの検索は、最初にオブジェクト自体から実行され、次にそのプロトタイプ、そのプロトタイプのプロトタイプなどから、プロトタイプチェーンが終了するまで実行されます。これは、オブジェクトとそのプロトタイプオブジェクトが順番にチェックされる、単一にリンクされたオブジェクトのリストのようなものです。これは、プロパティの継承が実装される方法です。たとえば、(ほとんどですが、後で詳しく説明します)任意のオブジェクトにメソッドvalueOf
とがありtoString
ます。
, constructor
__proto__
. constructor
-, , __proto__
( null, ). .
, .
constructor
constructor
– , :
const a = {};
a.constructor === Object // true
, , :
object.constructor(object.arg)
, , , . constructor
, writable , , , .
, , JS . , , [[SlotName]]
. [[Prototype]]
- ( null
, ).
- , [[Prototype]]
JS , . , __proto__
, , JS .
,
__proto__
[[Prototype]]
Object.prototype
:
- __proto__
. __proto__
, . __proto__
:
const foo = {};
foo.toString(); // toString() Object.prototype '[object Object]',
foo.__proto__ = null; // null
foo.toString(); // TypeError: foo.toString is not a function
foo.__proto__ = Object.prototype; //
foo.toString(); // , TypeError: foo.toString is not a function
? , __proto__
– Object.prototype
, foo
. - Object.prototype
, __proto__
.
. :
var baz = { test: "test" };
var foo = { bar: 1 };
foo.__proto__ = baz;
Chrome foo :
baz
Object.prototype
:
baz.__proto__ = null;
Chrome :
Object.prototype
baz
__proto__
undefined
foo
, Chrome __proto__
. [[Prototype]]
, __proto__
, , .
: .
: __proto__
Object.setPrototypeOf
.
var myProto = { name: "Jake" };
var foo = {};
Object.setPrototypeOf(foo, myProto);
foo.__proto__ = myProto;
, , .
[[Extensible]]
, . , false : Object.freeze
, Object.seal
, Object.preventExtensions
. :
const obj = {};
Object.preventExtensions(obj);
Object.setPrototypeOf(obj, Function.prototype); // TypeError: #<Object> is not extensible
. .
:
const foo = Object.create(myPrototype);
Object.create
, __proto__
:
const foo = { __proto__: myPrototype };
:
const f = function () {}
f.prototype = myPrototype;
const foo = new f();
new
, . , new
prototype
, .. [[Prototype]]
, .
.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const user = new Person('John', 'Doe');
Person , :
Person.prototype
? , prototype
(note 3), prototype
, . , :
Person.prototype.fullName = function () {
return this.firstName + ' ' + this.lastName;
}
user.fullName()
"John Doe".
new
new . new :
- self
- prototype self
- self this
- self ,
, new :
function custom_new(constructor, args) {
// https://stackoverflow.com/questions/31538010/test-if-a-variable-is-a-primitive-rather-than-an-object
function isPrimitive(val) {
return val !== Object(val);
}
const self = Object.create({});
const constructorValue = constructor.apply(self, args) || self;
return isPrimitive(constructorValue) ? self : constructorValue;
}
custom_new(Person, ['John', 'Doe'])
ES6 new new.target, , new, :
function Foo() {
console.log(new.target === Foo);
}
Foo(); // false
new Foo(); // true
new.target
undefined , new
;
, Student
Person
.
- Student Person
- `Student.prototype` `Person`
- `Student.prototype`
function Student(firstName, lastName, grade) {
Person.call(this, firstName, lastName);
this.grade = grade;
}
// 1
Student.prototype = Object.create(Person.prototype, {
constructor: {
value:Student,
enumerable: false,
writable: true
}
});
// 2
Object.setPrototypeOf(Student.prototype, Person.prototype);
Student.prototype.isGraduated = function() {
return this.grade === 0;
}
const student = new Student('Judy', 'Doe', 7);
( , .. this ), ( )
1 , .. Object.setPrototypeOf
.
, , Person Student:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
class Student extends Person {
constructor(firstName, lastName, grade) {
super(firstName, lastName);
this.grade = grade;
}
isGraduated() {
return this.grade === 0;
}
}
, :
- , new
-
prototype .
P.S.
1つの記事ですべての質問に答えることを期待するのは単純です。興味深い質問、歴史への遠足、私がすべて間違ったことをしたという合理的または根拠のない発言、またはエラーの修正がある場合は、コメントに書き込んでください。