アジェンダ
プロトタイプチェーンにおけるプロパティ探索の順序について簡単にまとめます。
プロパティ探索の順序
-
オブジェクト自身のプロパティ:
まず、オブジェクト自身が持っているプロパティが参照されます。もしそのプロパティが存在する場合は、その値が返されます。 -
オブジェクトのプロトタイプ (
__proto__
):
2オブジェクト自身に該当するプロパティがない場合、そのオブジェクトのプロトタイプオブジェクトが参照されます。このプロトタイプオブジェクトもまたオブジェクトであり、同じ手順でプロパティが探されます。 -
プロトタイプチェーンの遡り:
プロトタイプオブジェクトにも該当するプロパティがない場合、そのプロトタイプオブジェクトのプロトタイプ(つまり、プロトタイプのプロトタイプ)が参照されます。これが繰り返され、チェーンを辿っていきます。 -
最上位の
Object.prototype
:
最終的に、すべてのオブジェクトはObject.prototype
に辿り着きます。Object.prototype
には標準的なJavaScriptのオブジェクトが共通で持つメソッドやプロパティ(toString()
、hasOwnProperty()
など)が定義されています。 -
null
:
プロトタイプチェーンの最上位はnull
で、null
に達するとそれ以上プロトタイプチェーンを遡ることはできません。もし参照するプロパティがチェーン内のどのオブジェクトにも存在しない場合は、undefined
が返されます。
このように、プロトタイプチェーンはオブジェクト自身から始まり、順次プロトタイプを遡りながら該当するプロパティを探していく仕組みです。
実例:
先ほどの1から5の流れを実感するために、実例として以下の様なコードがあるとします。
このコードでは、Person
コンストラクタ関数を定義し、そのプロトタイプに greet
メソッドを追加しています。john
は Person
のインスタンスです。
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
return `Hello, my name is ${this.name}`;
};
const john = new Person('John');
では、上記john
オブジェクトを活用してプロパティやメソッドがどのように参照されるかを確認していきます。
1. オブジェクト自身のプロパティ
console.log(john.name); // 'John'
ここでは、john
オブジェクトに直接 name
プロパティが存在するため、John
が返されます。
2. オブジェクトのプロトタイプ (__proto__
)
console.log(john.greet()); // 'Hello, my name is John'
john
オブジェクトには greet
プロパティが存在しないため、Person.prototype
にある greet
メソッドが参照され、実行されます。
3. プロトタイプチェーンの遡り
console.log(john.toString()); // '[object Object]'
toString
メソッドは john
オブジェクトにも Person.prototype
にも存在しませんが、Person.prototype
のプロトタイプである Object.prototype
に定義されています。したがって、Object.prototype.toString
メソッドが実行されます。
4. 最上位の Object.prototype
console.log(john.hasOwnProperty('name')); // true
hasOwnProperty
メソッドも Object.prototype
に存在します。john
オブジェクトには直接存在しませんが、プロトタイプチェーンを遡って Object.prototype.hasOwnProperty
メソッドが呼び出されます。
5. null
プロトタイプチェーンの最後は null
に到達します。もし参照しているプロパティが Object.prototype
にも存在しなければ、undefined
が返されます。
console.log(john.nonExistentProperty); // undefined
johnオブジェクトを展開
john
オブジェクトを展開すると、以下のような形で表示されます。
これはオブジェクトの各プロパティとそのプロトタイプチェーンの構造を示します。
{
name: 'John',
__proto__: {
greet: function() { return `Hello, my name is ${this.name}`; },
__proto__: {
// Object.prototype のプロパティとメソッド
constructor: function Object() { ... },
hasOwnProperty: function hasOwnProperty() { ... },
isPrototypeOf: function isPrototypeOf() { ... },
propertyIsEnumerable: function propertyIsEnumerable() { ... },
toString: function toString() { ... },
valueOf: function valueOf() { ... },
// さらに他の Object.prototype のプロパティ
__proto__: null
}
}
}
-
name: 'John'
:-
john
オブジェクトに直接定義されているプロパティです。
-
-
__proto__
:-
john
オブジェクトのプロトタイプ (Person.prototype
) への参照です。ここにgreet
メソッドが定義されています。
-
-
Person.prototype
の__proto__
:-
Person.prototype
のプロトタイプはObject.prototype
です。ここにはconstructor
やtoString
などの標準的なJavaScriptのオブジェクトに共通のプロパティやメソッドが定義されています。
-
-
Object.prototype
の__proto__
:-
Object.prototype
のプロトタイプはnull
です。これはプロトタイプチェーンの最上位であり、これ以上遡ることはありません。
-
まとめ
プロパティが見つかるまでプロトタイプチェーンを順番に遡ることで、JavaScriptは適切なプロパティやメソッドを見つけ出します。