プロトタイプは初期化じゃない

関数のプロトタイプはインスタンスの初期化のためにあるわけではありません。


// コンストラクタ
function Func1() {
  return this;
}

// メンバ prop1
Func1.prototype.prop1 = 1;

このようなコンストラクタで実験をしてみます。

// インスタンス3つ作成
var x = new Func1();
var y = new Func1();
var z = new Func1();

// メンバを確認
alert(x.prop1);  // 1
alert(y.prop1);  // 1
alert(z.prop1);  // 1

// xのメンバを変更
x.prop1 = "ABC";

// 他には影響ない
alert(x.prop1);  // ABC
alert(y.prop1);  // 1
alert(z.prop1);  // 1

プロトタイプにprop1があることにより、newしたときに各インスタンスprop1が用意され、プロトタイプの値がコピーされているように見えますが、そうではありません。
残念なことに、x,y,zの各インスタンスの生成時にはメンバprop1は存在していません。
参照x.prop1などは、xにメンバprop1が存在しないので、xのコンストラクタのプロトタイプからprop1を探し出して返しています。
そして、x.prop1に値が代入されて初めて、xに直接のメンバprop1ができるのです。
例えば、上のコードの続きで

// プロトタイプを変更
Func1.prototype.prop1 = 24;

// 他への影響は?
alert(x.prop1);  // ABC
alert(y.prop1);  // 24
alert(z.prop1);  // 24

と確かめることができます。

「プロトタイプ」という言葉を使わないならば、「初期化」というよりも「暗黙の参照」というとらえ方が近いと思われます。


ここまでならば、多少JavaScriptを使っている方ならば知っている範囲だと思います。
では、プロトタイプの値(上記の例でいうFunc1.prototype.prop1)を、(Func1.prototype.prop1のように)prototypeにアクセスせずに変更するにはどうしたらいいでしょうか?

何段も継承している場合、どの関数のprototypeのメンバかわからないのです。

答えは簡単で、


function Func1() {
  return this;
}

Func1.prototype.prop1 = 1;
Func1.prototype.setProp1 = function(val) {
  Func1.prototype.prop1 = val;
};

このようにアクセッサメソッドもprototypeに付けるだけです。
使い方は

var x = new Func1();
var y = new Func1();

alert(x.prop1);  // 1
alert(y.prop1);  // 1

x.setProp1(17);

alert(x.prop1);  // 17
alert(y.prop1);  // 17

のようになり、どの関数のprototypeのメンバであるかを意識する必要がなくなります。


これだけでは「当たり前じゃん」の世界なのですが、CPTで何段も継承したクラスを作っていると、これがあなどれなくなってくるのです。