Javascript初心者からみたprototype

http://d.hatena.ne.jp/amachang/20070413/1176421425
勉強会のあと、そしてこの記事の後、
もう一度自分の中でprototypeについてまとめてみた。

下の実行結果を見て、首をかしげた人は是非とも読んでいただければと思う。
(って、首をかしげるのは僕だけか?!)

var Box = function(size){ this.size = size; };
Box.prototype = { color: "red" };

var box = new Box(10);
alert(box.color); // red

Box.prototype = { color: "blue" };
alert(box.color); // red

var box2 = new Box(11);
alert(box2.color); // blue

Box.prototype.color = "yellow";
alert(box.color);   // red
alert(box2.color);  // yellow

僕は最初、この結果を理解することができなかった。
というのも、Boxというクラスから作られた2つのオブジェクトにおいて、
クラスの元となるオブジェクトおよびクラス変数を変更しても、
想定する挙動を示してくれないと思ったからだ。

以下が想定してた挙動との相違点。

#1
Box.prototype = { color: "blue" };
alert(box.color); // red (想定してたのは blue)

#2
Box.prototype.color = "yellow";
alert(box.color);   // red (想定してたのはyellow)
alert(box2.color);  // yellow (想定とおり)

この相違によって、結果的に僕のJavascriptに対する理解を深めることができた。
それは、
Box.prototypeが指しているオブジェクトが不変であるという間違った認識に気づいたことであった。

より分かり易い例を下に示す。

var Box = function (size) { this.size = size; };
Box.prototype = { color: "red" };

Boxのprototypeには「赤い箱」がセットされる。
この後、Boxのオブジェクトを作成すると、「赤い箱」が作成できる。

var redBox1 = new Box(10);
var redBox2 = new Box(11);

続いて、Boxのprototypeに「青い箱」をセットしよう。
(この処理をやってはいけないかどうかの問題はさておいて...)
そして、Boxのオブジェクト(青い箱)を作成する。

Box.prototype = { color: "blue" };
var blueBox = new Box(12);

確認すると以下の結果が得られる。(当然と思われるかもしれない、、)

alert(redBox1.color) // red
alert(redBox2.color) // red
alert(blueBox.color) // blue

//redBox1.__proto__ => { color: "red" }
//redBox2.__proto__ => { color: "red" }
//blueBox.__proto__ => { color: "blue" }

続いて、Box.prototypeのcolorプロパティを"yellow"に変更する。

Box.prototype.color = "yellow";

Box.prototypeには「青い箱」がセットされており、
その青い箱を元に、blueBox というオブジェクトが作られていたことを思い出す。

つまり、無名オブジェクト{ color: "blue" }には今、
Box.prototype と blueBox.__proto__ でアクセスすることができるのだ。
そのcolorプロパティを "yellow"で上書きするのがこの処理である。

alert(blueBox.color) //yellow

青い箱には全く関連のないredBoxには影響はない。

alert(redBox1.color) // red
alert(redBox2.color) // red

首を傾げていた方はすっきりしたかもしれない。
つまり、new Box から作成されたオブジェクトは、
そのときのBox.prototypeに入っていたオブジェクトを常に(prototypeとして)持ち続けるということだ。

そして、Box.prototypeに新しいオブジェクトを入れ替えたところで、
既にnew されて作成されたオブジェクトのプロパティには何ら影響はない。

もちろん、Box.prototypeに入っているオブジェクトと、そのオブジェクトのprototypeが一致している場合には、
(Box.prototypeに全く新しいオブジェクトを入れ直してない限りは)、
Box.prototypeに処理を加えれば、オブジェクトのprototypeにも処理を加えることになる。

だから、「青い箱」だけが「黄色い箱」に変わることができたのだ。

弾さんがおっしゃられていたObject.prototype = {} を(特に大規模開発で)使用すべきでないというのは、
prototypeに異なるオブジェクトを代入すると、
代入前にnewされて作成されたオブジェクトと、代入後にnewされたオブジェクトでは全く関連がなくなる、
ということを懸念してのものだったのかな、、と初心者なりに解釈してみた。

でも、Object.prototype = {}はやっぱり見やすいので、管理できる範囲であれば使いたい、とも思う。
そして何より、Javascriptギザオモシロス。