プロトタイプチェーンによる継承方法
暴露話から…。
私はここを参考にしてCPTの継承システムを作りました。
http://www.graviness.com/virgo/javascript/d020525.html
Virgo*1でこの記事をみて感服しました。
単なる「パクリ」ではもったいないし失礼だろうと思い、これを発展させてStaticについても継承させる作りにしました。
もともと、クラス(つまり、関数)所属のメンバやメソッドも継承させたかったのです。
// スーパークラス
function SuperClass() {
return this;
}SuperClass.prototype.protoMethod = function() {
alert("SuperProto!");
};
// ↓これを継承させたい
SuperClass.classMethod = function() {
alert("SuperClass!");
};
// サブクラス
function SubClass() {
SuperClass.apply(this, arguments);
return this;
}
// ここで継承(上記と全く同じ方法なので省略)
// これはできる
(new SubClass()).protoMethod();
// これがしたい
SubClass.classMethod();
しかし、ここで壁にぶち当たります。SubClass.classMethodがプロトタイプチェーンでSuperClass.classMethodを参照するには、
SubClassのコンストラクタのprototypeがSuperClassでなければなりません。
しかも、コンストラクタは内部的に持っているコンストラクタで、SubClass.constructorを付け替えても無駄でした。
そうすると、Function.prototypeがSuperClassである必要があります。
考えた解決案とその結果は
- たった1つのクラス間継承のためにFunction.prototypeを付け替える。
→そんなことはできません。 - SubClass.classMethodの実行中のみFunction.prototypeを付け替える。
→そのためにはSubClass.classMethodそのものを作らなければならないので本末転倒。 - クラスは関数ではなくオブジェクトにする。
→newできなくなってしまう。(クラス名.create()でも可能だが、newしたい!!)
そして最終的に導いた結論が、
「prototype」と同じようなプロトタイプチェーンで結ばれたオブジェクト*2をもう1つ作ってしまえばいい
でした。(これをCPTコアで実装しています。ソースを見ればすぐ発見できると思います。)
※ちなみにprototype.jsはプロトタイプチェーンを使った継承ではない・・・と、同じように考えている方もいるようですね。
http://d.hatena.ne.jp/amachang/20060227
06/03/21追記
どうやら私はprototype.jsを少しばかり誤解していたようです。
prototype.jsの継承メソッド
を見て、「なんだ、浅いコピーしちゃってるじゃん」と思いましたが、このメソッドを呼ぶ側でnewしてから使うわけですね。*3
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}
また、継承じゃなくて単にメンバを追加するときはnewせずに使う・・・ということでしょうか。
なるほど、そう使うのか。