JavaScriptの俺々継承 (IE10以下は非対応)
対象読者: JavaScriptで綺麗に継承を書きたい方。プロトタイプやプロトタイプチェーンがある程度わかる方。
JavaScriptの継承はいつもこんな感じで書きます。
__proto__を直接書き換えているので、IEは11以上じゃないと動きません。Firefox、Chrome、Safariは大体大丈夫だと思います。Operaは使ったことありません。
JavaScriptの継承(関数)
var staticInheritWithApply = function staticInheritWithApply(ChildObj, args, ParentCtor) { var p, ChildCtor; if (typeof ParentCtor === 'function' && typeof ChildObj === 'object' && ChildObj) { p = new ParentCtor(); ChildCtor = ChildObj.constructor; } else { return false; } ParentCtor.apply(ChildObj, args); //親コンストラクタのプロパティを引き継ぐ。次の行より前に書かなければならない。(必須) ChildCtor.prototype.__proto__ = ParentCtor.prototype; //子コンストラクタのプロトタイプの"参照先"をデフォルト(Object.prototype?)から親コンストラクタのプロトタイプに変更する。(必須) // ↑ここまで必須。↓ここからオマケ。詳しくはFirebugのDOMで参照してください。 ChildCtor.prototype.parentPrototype = ParentCtor.prototype; ChildObj.parentPrototype = ParentCtor.prototype; ChildCtor.prototype.parentObject = p; ChildObj.parentObject = p; return true; };
使い方の例(コンストラクタ→クラスと言い換え)
{ //個人的にクラスはこういう風に書きます。継承をクラスの内側に書くのがコツです。 var B = function B(hash, x, y, z) { var InheritFrom = A; //Aという親クラスを継承しますよということ。 staticInheritWithApply(this, arguments, InheritFrom); //ここ継承!thisとargumentsは決まり文句。 //xへの代入はクラスAに記述があればそちらが適用される。 this.y = getHashValue("y") || y || 0; //getHashValueは書いてないけど自作関数。 this.z = getHashValue("z") || z || 0; //引数が与えられなければこの場合0を代入。 }; B.prototype.something = function () { ... }; //共通処理はプロトタイプに書く。 B.prototype.anything = function () { ... }; //メモリの節約になる(らしい) } var b1 = new B({x: 1, y: 2, z: 3}) //ハッシュを使って初期化。 var b2 = new B(null, 1, 2, 3) //ハッシュを使わないで初期化。どちらでも同じ。 B.prototype.newfunction1 = function () { ... }; //もちろんb1とb2で使える。 A.prototype.newfunction2 = function () { ... }; //当たり前だけどb1とb2で使える。 alert(b1.parentObject.y); //上書きされる前のyの値を取得することもできる。
継承に関する記述は staticInheritWithApply() だけです。CがBを継承してBがAを継承するみたいな感じで、何段階でも継承できます。
Applyの連鎖によって親クラスの変数を全て自分のものにして、プロトタイプチェーンによって自身や親クラスの共通処理(大体は関数)が全て使えるようになります。ポイントはprototypeを一切上書きしないことです。
親にあるけど子にも同じ名前の変数や関数がある場合は、もちろん子で上書きされます。
staticInheritWithApply() の定義の中で親オブジェクトを参照するプロパティ(parentObjectとparentPrototype)を追加しているので、上書きされる前の親の値を取得することもできます。
クラスの外ではなく内側で継承の記述を書いているのがわかりやすくて気に入っています。動的に継承?したいならともかく、まとめられるものはなるべくまとめて書きたいですね。
これなら例えばクラスAとクラスBを書く場所が入れ替わっても、継承はクラスの内側に書かれているので何の問題も起きません。
ちなみに最初にも書きましたがIE10以下では動きません。