前の関連記事:JavaScriptプロトタイプチェーン(4)クラスの継承(Classical Inheritance)を図示する
自分がなかなかプロトタイプチェーンを理解できなかったのは内部プロパティ[[Prototype]]と関数オブジェクトのprototypeプロパティを混同していたのが原因でした。さらに関数オブジェクトを作ったときに暗黙的に生成されるその関数オブジェクトのプロトタイプオブジェクトと関数オブジェクトのprototypeプロパティも混同していたこともさらに理解を妨げていました。
プロトタイプチェーンをつなぐ内部プロパティ[[Prototype]]
プロトタイプチェーンは内部プロパティ[[Prototype]]で実現されています。(JavaScriptプロトタイプチェーン(3)すべてのプロトタイプチェーンはObject.prototypeオブジェクトで終わる)
内部プロパティ[[Prototype]]がnullになるまでプロトタイプチェーンは続きます。
Object.prototypeの[[Prototype]]はnullなので、[[Prototype]]を変更することがなければすべてのプロトタイプチェーンはObject.prototypeで終わることになります。
内部プロパティ[[Prototype]]はECMAScript 5まではアクセスする方法は定義されていませんでしたが、ほとんどのブラウザでは__proto__プロパティ(Object.prototype.__proto__)としてアクセスできました。
ECMAScript 6ではObject.setPrototypeOf()が定義されました。
しかし内部プロパティ[[Prototype]]を変更することは非推奨とされています。
実際この操作はかなり遅いです(Object.create vs Crockford vs Constructor · jsPerf)。
関数オブジェクトの暗黙的に生成されるプロトタイプオブジェクト
関数を定義すると暗黙的にその関数のプロトタイプオブジェクトが暗黙的に生成されます。
(JavaScriptプロトタイプチェーン(4)クラスの継承(Classical Inheritance)を図示する)
関数Car()を定義するとCar.prototypeが暗黙的に生成されます。
クラスモデルの継承を行うときはこのプロトタイプオブジェクトのプロトタイプチェーンを利用します。
関数オブジェクトの内部プロパティ[[Prototype]]はFunction.prototypeになっています。
Function.prototypeの内部プロパティ[[Prototype]]はObject.prototypeになっています。
プロトタイプオブジェクトを入れる関数オブジェクトのprototypeプロパティ
関数オブジェクトのprototypeプロパティには暗黙的に生成されたプロトタイプオブジェクトが入っています。関数オブジェクトModel()のprototypeプロパティにはModel.prototypeが入っています。
Model.prototype.getBrand = function () { return this.brand; }このときの「Model.prototype」はModel.prototypeというオブジェクトを指しており、Model.prototypeオブジェクトのgetBrandプロパティに値(このコードの場合は関数オブジェクト)が入ります。
Model.prototype = new Car;このときの「Model.prototype」はModel.prototypeというオブジェクトではなくModelオブジェクトのprototypeプロパティを指しており、関数オブジェクトのプロトタイプオブジェクトが元のModel.prototypeからnew Carに置換されたModel.prototypeになります。
これら3つの"プロトタイプ"を理解してネット上にいろいろあるブログの解説を読み直すと、「奇妙な現象」の原因がわかるようになりました。
参考にしたサイト
Object Playground: The Definitive Guide to Object-Oriented JavaScript
JavaScriptプロトタイプチェーン図示ツール。
JavaScript | MDN
Mozilla Developer NetworkによるJavaScriptのマニュアル。
Object.create vs Crockford vs Constructor · jsPerf
プロトタイプを指定したオブジェクト作成方法の速度比較。
0 件のコメント:
コメントを投稿