JavaScriptにおけるプロトタイプ

投稿者: | 2016-05-17

ついにプロトタイプです。JavaやC#ばっかり触っていた身としてはかなりとっつき辛い部分ではあったのですが、なんとか受け入れることができました。

プロトタイプ

JavaScriptにおける関数は、オブジェクトの生成時に自動的にprototypeプロパティが付与されます。prototypeプロパティは、ただの関数定義の場合はプロパティの値は空のオブジェクトとなります(厳密には列挙不可なconstructorプロパティを持っています)。また、関数(すなわちprototypeプロパティを持ったオブジェクト)をコンストラクタとしてnew演算子を使ってインスタンスを生成すると、そのインスタンスは__proto__プロパティを通じてコンストラクタ関数のprototypeプロパティとリンクします。

var myArray = new Array();
// これらはすべて同じオブジェクトを指している
console.log(myArray.__proto__);
console.log(myArray.constructor.prototype);
console.log(Array.prototype);

このprototypeプロパティが示すオブジェクトがプロトタイプです。インスタンスはプロトタイプが持つプロパティにアクセスすることができます。ですのでプロトタイプはインスタンスの雛型のようなものであり、JavaやC#でいうところのクラス定義に相当するものです(あくまでもイメージで厳密には異なるものです)。

プロトタイプチェーン

次のコードを見てください

var myArray = new Array();
// これらはすべて同じオブジェクトを指している
console.log(myArray.__proto__); // ArrayのインスタンスのプロトタイプはArray.prototype
console.log(myArray.__proto__.__proto__);   //Array.prototypeのプロトタイプはObject.prototype

このコード結果より、Array()から生成したインスタンスはArrayプロトタイプを雛形としており、またArrayプロトタイプはObjectプロトタイプを雛形としています。このつながりをプロトタイプチェーンと呼びます。プロパティにアクセスする際、このプロトタイプチェーンをたどってプロパティを検索し、最初に見つかった個所のプロパティを適用します(スコープチェーンと似たような動作ですね)。

プロトタイプ継承

JavaScriptのオブジェクトを継承すると、継承元のオブジェクトのプロトタイプが持つプロパティも継承先から利用できるようになります(これはJavaやC#でも同じですね)。JavaScriptの言葉で表現するならば、プロトタイプチェーンの順番に従ってプロパティ名を検索する、といったところでしょうか。

var highObj = {};                           // Object.prototypeを継承したオブジェクト
highObj.x = 1;                              // 独自プロパティxを持つ
var middleObj = Object.create(highObj);     // middleObjはhighObjとObject.prototypeのプロパティを継承したオブジェクト
middleObj.y = 2;                            // 独自プロパティyを持つ
var lowObj = Object.create(middleObj);      // lowObjはmiddleObjとhighObjとObject.prototypeのプロパティを継承したオブジェクト
lowObj.z = 3;                               // 独自プロパティzを持つ

console.log(lowObj.x + lowObj.y);           // 継承先から継承元へプロパティを検索する。1 + 2 = 3が表示される

highObj.x = 3;                              // 継承元オブジェクトのプロパティを変更すると
console.log(lowObj.x);                      // 継承先のプロパティにも繁栄される

lowObj.x = 4;                               // 継承先で継承元のプロパティを設定すると、
console.log(highObj.x);                     // 継承元には反映されない。3が表示される
console.log(lowObj.x);                      // 継承先のプロパティが継承元のプロパティを隠蔽する。4が表示される

Javaの場合は、スーパークラスとサブクラスで同じ名前のプロパティが存在する場合、プロパティ値は変数の型に依存しますが、JavaScriptは変数の型付けが弱いので動作が異なります。継承元のプロパティをオーバーライドするようなイメージでしょうか。

まとめ

今回にてJavaScriptまとめはいったんストップです。これからは具体コードを書いていきたいと思います。