Javaに慣れたからちょっと理解しづらかった
プロトタイプ
JavaScriptでは、全てのものがオブジェクト(またはそのインスタンス)である
そして、全てのオブジェクトはprototypeオブジェクトおよび__proto__プロパティを持っている
-
__proto__: これはポインタみたいなプロパティである。どこかのprototypeを指している -
prototype: 子オブジェクトに継承させたいものをこの中に記述する。(インスタンスはこれを持たない)prototypeもオブジェクトなので、その中にも__proto__プロパティがある
プロトタイプチェーン
あるインスタンスから、メソッドを呼び出す時の話。
もしインスタンス自身にメソッドが見つからない場合、
そのインスタンスの__proto__プロパティから、どこかのprototypeに辿り、その中で探す。
それでもない場合、さらにそのprototype内の__proto__より、新たなprototypeに辿り着く。
こうやって繰り返して、最終的に__proto__ === nullとなり、undefinedが返される。
具体的な関係
Object.prototype.__proto__ === null
Object.__proto__ === Function.prototype
// 生成したインスタンスの__proto__
(new Object()).__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
Function.__proto__ === Function.prototype
// 生成したインスタンスの__proto__
(new Function()).__proto__ === Function.prototype
Array.prototype.__proto__ === Object.prototype
Array.__proto__ === Function.prototype
// 生成したインスタンスの__proto__
(new Array()).__proto__ === Array.prototype
自作コンストラクタで生成したインスタンス
例えば下のようなコンストラクタでインスタンスを生成したとする
function Person(name){
this.name = name;
}
const person = new Person("yuki");
関係性は以下のようになる
person.__proto__ === Person.prototype
Person.prototype.constructor.__proto__ === Function.prototype
Person.prototype.constructor.prototype === Person.prototype
Object.create()で生成したインスタンス
Object.create()を使ってもインスタンスを生成できる
const anotherPerson = Object.create(person);
このとき、anotherPerson.__proto__が参照しているのは、personインスタンスとなる
つまり、personに何かを追加すれば、anotherPersonもそれを使える(anotherPerson.__proto__で探す)
Person.prototypeに追加して、anotherPersonでも使える(anotherPerson.__proto__.__proto__で探す)
=で生成したインスタンス
const person01 = person
Object.create()と違って、person01.__proto__ === person.__proto__ === Person.prototype
コンストラクタ
コンストラクタ関数の値は、prototype内のconstructorプロパティに保存されている
インスタンスからは、インスタンス.constructorでアクセスできる(実際はインスタンス.__proto__.constructor)
こういうことを利用して、インスタンスからコンストラクタを入手し、それで新しいインスタンスを生成できる
const person02 = new anotherPerson.constructor("abc");
person02.__proto__ === Person.prototype // true
anotherPerson.__proto__が参照しているのは、personインスタンス
しかし、constructorというプロパティはanotherPersonにもpersonにもなく、
結局Person.prototypeまで探しに行ったので、person02.__proto__ === Person.prototypeがtrueとなる
constructor.name
インスタンス.constructor.nameで、コンストラクタ関数の関数名を取得できる
プロトタイプの変更
コンストラクタ関数のprototypeプロパティを変更すれば、
コンストラクタから作成されたすべてのオブジェクトインスタンスで使用可能になる
ただし、prototypeに追加した関数内部で、メンバのプロパティを利用したいなら、
thisキーワードを使う必要がある
参考記事
Object のプロトタイプ
図で理解するJavaScriptのプロトタイプチェーン
JavaScriptのプロトタイプチェーンを深堀りする
JavaScript のプロトタイプを理解する
JavaScriptのプロトタイプからオブジェクト指向を学ぶ