アジェンダ
プロトタイプチェーンにおけるプロパティ探索の順序について簡単にまとめます。
プロパティ探索の順序
-
オブジェクト自身のプロパティ:
まず、オブジェクト自身が持っているプロパティが参照されます。もしそのプロパティが存在する場合は、その値が返されます。 -
オブジェクトのプロトタイプ (
__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は適切なプロパティやメソッドを見つけ出します。