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のプロトタイプからオブジェクト指向を学ぶ