JavaScriptプロトタイプチェーン(3)すべてのプロトタイプチェーンはObject.prototypeオブジェクトで終わる

ラベル:

前の関連記事:JavaScriptプロトタイプチェーン(2)プロトタイプチェーンとプロパティシャドウィング


すべてのプロトタイプチェーンは内部プロパティ[[Prototype]]でつながり、Object.prototypeオブジェクトで終わることをObject Playgroundで図示して確認します。

「Show standard objects」をチェックしてプロトタイプチェーンを終点まで図示する

var parent = {
    get: function fn() {
         return this.val;
    },
    val: 42
}
var child = Object.create(parent);
child.val = 3.14159
this.parent = parent;
this.child=child;

Object Playgroundはデフォルトでは基本オブジェクトのノードは表示されません。

上図のグラフの場合、thisオブジェクトのノードとthis.parentオブジェクトのノードの一番下にあるObject.prototypeのObjectが基本オブジェクトに該当します。

プロトタイプチェーンの定義ではこの部分がnullになるノードにたどり着くまでチェーンが続くはず(Inheritance and the prototype chain)なのでこのグラフのプロトタイプチェーンは終点まで表示されていないことになります。

そこで「Show standard objects」をチェックして「Click to Evaluate」します。


Object.prototypeオブジェクトへのエッジが表示されました。

Object.prototypeオブジェクトのconstructorプロパティから関数オブジェクトObject()へ続くエッジもあるのですが今回は割愛してあります。

Object.prototypeオブジェクトのノードの一番下の項目をみるとnullになっていますので、このノードがプロトタイプチェーンの終点になります。

プロトタイプチェーンは内部プロパティ[[Prototype]]でつながっている


プロトタイプチェーンはこれまでみてきたように、各オブジェクトのプロトタイプをたどってプロパティを探してきます。

各オブジェクトのプロトタイプは内部プロパティの[[Prototype]](8.6.2 Object Internal Properties and Methods)で定義されています。

なのでObject Playgroundのグラフのオブジェクトのノードの一番下のエッジがつながっている項目が内部プロパティ[[Prototype]]ということになります。

Object Playgroundにあるビデオの中のグラフは[[Prototype]]と書いてあります。

内部プロパティ[[Prototype]]を変更することはできるけど変更することは非推奨


JavaScriptの標準を定めているECMAScript Language Specification - ECMA-262 Edition 5.1(ECMAScript 5.1)ではこの内部プロパティ[[Prototype]]の値を得る関数Object.getPrototypeOf ( O )が定められています。

[[Prototype]]の値を変更する関数はECMAScript 5.1では定義されていなかったのですが、その値を変更できるプロパティ__proto__を実装しているブラウザがあまりに多いためECMAScript 2015 Language Specification – ECMA-262 6th Edition(ECMAScript 6)ではObject.prototype.__proto__が定義されました。(Object.prototype.__proto__ - JavaScript | MDN

ECMAScript 6 compatibility tableをみるとIE10を除いてNode.jsを加えてすべてのブラウザがObject.prototype.__proto__を実装しています。
var parent = {
    get: function fn() {
         return this.val;
    },
    val: 42
}
var child = Object.create(parent);
child.val = 3.14159
parent.__proto__=null;
this.parent = parent;
this.child=child;
parent.__proto__をnullに変更してみました。

(グラフを一部省略しています。)

ちゃんとthis.parentオブジェクトのノードの最下段の項目がnullになってプロトタイプチェーンがそこで終わっています。

このように内部プロパティ[[Prototype]]を変更することはとても時間がかかりパフォーマンスが落ちるので推奨されていません。

ECMAScript 6で__proto__プロパティが定義されたにもかかわらずそれは単なる互換性の維持のためだけであってその使用は非推奨であり、どうしても[[Prototype]]を変更したいときはECMAScript 6で定義されたObject.setPrototypeOf()を使うことが推奨されています。
var parent = {
    get: function fn() {
         return this.val;
    },
    val: 42
}
var child = Object.create(parent);
child.val = 3.14159
Object.setPrototypeOf(parent, null)
this.parent = parent;
this.child=child;
これでもparentオブジェクトの[[Prototype]]をnullに変更できます。

Object.setPrototypeOf()を使ったにしろパフォーマンスが落ちることには変わりなく、[[Prototype]]を変更するのではなく、Object.create()を使って望みの[[Prototype]]をもつオブジェクトを新たに作成することが推奨されています。

参考にしたサイト


Object Playground: The Definitive Guide to Object-Oriented JavaScript
JavaScriptプロトタイプチェーン図示ツール。

JavaScript | MDN
Mozilla Developer NetworkによるJavaScriptのマニュアル。

ECMAScript Language Specification - ECMA-262 Edition 5.1
JavaScriptが準拠しているECAMScript5.1

ECMAScript 2015 Language Specification – ECMA-262 6th Edition
JavaScriptが準拠しているECAMScript6

ECMAScript 6 compatibility table
ECMAScript6の互換性一覧表。互換表をみているブラウザ自体の互換性も左端に表示されます。

次の関連記事:JavaScriptプロトタイプチェーン(4)クラスの継承(Classical Inheritance)を図示する

PR

0 件のコメント:

コメントを投稿