Javascript でオブジェクトを new するとき、プロトタイプのプロパティは単にコピーされるだけで、初期化はされない。これに気づかずに一日無駄にしたorz 例を挙げると、
var MyObj = function(){};
MyObj.prototype = {
a: function(){
alert( this.b.join( " : " ) );
}
,b: [ 1, 2 ]
};
var obj1 = new MyObj();
obj1.a(); // 「1 : 2」と表示される
var obj2 = new MyObj();
obj2.b.push( 3 );
obj2.a(); // 「1 : 2 : 3」と表示される
var obj3 = new MyObj();
obj3.b.push( 4 );
obj3.a(); // ????
18 行目で表示されるのは次のどちらだろうか。
- 1 : 2 : 4
- 1 : 2 : 3 : 4
答えは後者、“1 : 2 : 3 : 4”である。
MyObj.prototype.b に代入された“[ 1, 2 ]”は配列オブジェクトへのリファレンスを表している(6 行目)。new すると、配列オブジェクトがクローンされるのではなく、リファレンスがコピーされる(12 行目)だけなので、それに push する(13 行目)とオブジェクトのプロトタイプ自体をいじることになってしまう。結果、次に new したとき(16 行目)には、すでに改変された MyObj.prototype.b を“継承”することになるのだ。
これを避けるには、コンストラクタでいちいち初期化するようにすればよい。次のように書くと、new する度に新鮮な配列オブジェクトが代入されるので、上に書いたような汚染は起こらない。
var MyObj = function(){
this.b = [ 1, 2 ];
};
MyObj.prototype = {
a: function(){
alert( this.b.join( " : " ) );
}
};
var obj1 = new MyObj();
obj1.a(); // 「1 : 2」と表示される
var obj2 = new MyObj();
obj2.b.push( 3 );
obj2.a(); // 「1 : 2 : 3」と表示される
var obj3 = new MyObj();
obj3.b.push( 4 );
obj3.a(); // 「1 : 2 : 4」と表示される
このような問題が起こるのは、プロトタイプのメンバにオブジェクトを設定したときだけである。リテラルを定数として設定するだけなら問題ないし、その方が可読性は上がるだろう。
var MyObj = function(){
this.b = [ 1, 2 ];
};
MyObj.prototype = {
a: function(){
alert( this.b.join( " : " ) );
}
,name: "delphinus"
,species: "human"
};
