takafumi blog

日々の勉強メモ

【JavaScript】 プロトタイプ/プロトタイプチェーン

環境   CentOS 7.0 JavaScript Node.js 0.12.2

プロトタイプと通常のプロパティの違い
// prototypeを設定
var Hoge = function() {}
Hoge.prototype.a = 1;

// プロパティを設定
var Fuga = function() {
      this.a = 1;
}

var hoge0 = new Hoge();
var fuga0 = new Fuga();

console.log(hoge0.a);  // 1
console.log(fuga0.a);  // 1

これだけ見ると、結果は全く同じになる。

では何が違うのか?

以下のようにしてみる。

var fuga1 = new Fuga();
var fuga2 = new Fuga();

// fuga1.aプロパティに2を設定
fuga1.a = 2;

// 各プロパティはインスタンス毎に、別に保持されているので、当然値は異なる
console.log(fuga1.a);  // 2
console.log(fuga2.a);  // 1
var hoge1 = new Hoge();
var hoge2 = new Hoge();

// これはprototypeを参照している
console.log(hoge1.a);  // 1

// プロパティにa = 2を設定
hoge1.a = 2;
// prototypeではなく、プロパティaを参照
// このときprototypeは見えなくなる
console.log(hoge1.a);  // 2

// プロパティaを削除
delete hoge1.a;

// 再びprototypeが見える。hoge2も同じくprototypeが見える
console.log(hoge1.a);  // 1
console.log(hoge2.a);  // 1

// ここで、prototypeの値を変更する
Hoge.prototype.a = 3;

// prototypeを参照する、2つの値が両方とも変更されている
console.log(hoge1.a);  // 3
console.log(hoge2.a);  // 3

これからわかるのは次の2点

  1. prototypeと同じ名前のプロパティが設定されると、prototypeはプロパティに隠され見えなくなる。
    ただし、その場合もprototypeは削除されたわけではい。
  2. インタンスはprototypeの参照を持っている。
    そのため、prototypeが変更されると、そのインスタンスのプロパティやメソッドは全て変更される。

f:id:takafumi-s:20150608230150p:plain


プロトタイプチェーン
var Animal = function(){};
Animal.prototype = {
      category : function(){return '哺乳類';}
}

var Dog = function(){};
Dog.prototype = new Animal();
Dog.prototype.type = function(){return '犬';}

var d = new Dog();
console.log(d.type());     // 犬
console.log(d.category()); // 哺乳類

つまり、もしインスタンスメソッドにも、そのクラスのprototypeメソッドにも、指定のメソッド(ここではcategory())が無いとき、自動的にprototypeをさかのぼりスーパークラスのprototypeを探す。

これをプロトタイプチェーンと呼ぶ。

クラスがパッと見、functionとなっていて、JavaPHPなどと異なって見えるが、用はサブクラスのインスタンスが、サブクラスにメソッドが定義されていないときに、スーパークラスを探しに行く、という一般的な仕組み。

一般的な言語と大きく異なるのは、インスタンスを生成後に、継承されたメソッドやプロパティを変更する事ができる事だ。上記の例で、

console.log(d.type());     // 犬
console.log(d.category()); // 哺乳類

// Animalのprototpyeを変更
Animal.prototype.category = function(){return 'Mammal';}
console.log(d.category()); // Mammal

このように継承元のprototypeを変更すれば、参照しているだけのサブクラスのインスタンスのprototpyeメソッドやプロパティも変更される。