prototype.jsでの継承を楽にする

Mochikitも好きですが、普段はRails使いということもあってprototype.jsを使っています。

そういえばjavascriptのライブラリでOOなもの、ということで初めて出会ったのは jsolait でした。 当時の印象としては「んー確かにがんばってるんだけど・・・ちょっと大げさっていうか・・・」って感じでした。

そして去年、prototype.jsに出会うわけですが「class-style OO」とうたっている部分がかなりシンプル(すぎ)で衝撃でした。

ただ、やっぱりjavascriptにおいて「継承」というのはやっかいな問題です。 というわけで誰か作ってると思うんですが、より簡単に継承できるライブラリを作ってみました。

 1Class.def = function(definition) {
 2  var klass = Class.create();
 3  if(definition.__extend__)
 4    Class.inherit(klass, definition.__extend__);
 5  if(definition.__static__)
 6    Object.extend(klass, definition.__static__);
 7  if(definition.__include__) {
 8    if(definition.__include__.constructor != Array)
 9      definition.__include__ = [definition.__include__];
10    definition.__include__.each(function(include){
11      Object.extend(klass.prototype, include);
12    });
13  }
14  ["__static__","__extend__","__include__"].each(function(n){delete definition[n];}
15  Object.extend(klass.prototype, definition);
16  return klass;
17}    
18
19Class.inherit = function(child, parent) {
20  Object.extend(child, parent);
21  child.__super__ = parent.prototype;
22  var dummy = function(){};
23  dummy.prototype = parent.prototype;
24  child.prototype = new dummy();
25  child.prototype.constructor = child;
26  return child;
27}

こんだけです。 使い方はサンプルをみるほうがわかりやすいと思うので、サンプルをどーぞ。

 1var IncludedClass = Class.def({
 2  __static__ : {
 3    includedMethod : function() {
 4      return "includedMethod:"+this.value;
 5    }
 6  }
 7});    
 8
 9var AClass = Class.def({__include__ : IncludedClass,
10  __static__ : {
11    A : "A static"
12  },
13
14  initialize : function(val) {
15    if(!val) throw "error";
16    this.val = val;
17    this.klass = "AClass";
18  },
19
20  inspect : function() {
21    return "< "+this.klass+" : "+this.val+">";
22  }
23});
24
25var BClass = Class.def({ __extend__ : AClass,
26  __static__ : {
27    B : "B static"
28  },
29
30  initialize : function(val) {
31    BClass.__super__.initialize.call(this, val);
32    this.klass = "BClass";
33  }
34});
35
36var CClass = Class.def({ __extend__ : BClass,
37  __static__ : {
38    C : "C static"
39  },
40
41    initialize : function(val) {
42      CClass.__super__.initialize.call(this, val);
43      this.klass = "CClass";
44    }
45});
46
47var a = new AClass("aaaaaaa");
48var b = new BClass("bbbbbbb");
49var c = new CClass("ccccccc");
50alert("a inspect:"+a.inspect()); //=> a inspect:
51alert("b inspect:"+b.inspect()); //=> b inspect:
52alert("c inspect:"+c.inspect()); //=> c inspect:
53alert("includedMethod:"+a.includedMethod()); //=> includedMethod:included aaaaaa
54alert("CClass.A:"+CClass.A); //=> CClass.A:A static
55alert("CClass.B:"+CClass.B); //=> CClass.B:B static
56alert("CClass.C:"+CClass.C); //=> CClass.C:C static
57alert("b.constructor = a.constructor:"+(b.constructor == a.constructor).toString()); //=>b.constructor = a.constructor:false
58alert("b instanceof BClass:"+(b instanceof BClass).toString()); //=>b instanceof BClass:true
59alert("b instanceof AClass:"+(b instanceof AClass).toString()); //=>b instanceof AClass:true
60alert("c instanceof BClass:"+(c instanceof BClass).toString()); //=>c instanceof BClass:true
61alert("c instanceof AClass:"+(c instanceof AClass).toString()); //=>c instanceof AClass:true</div>

継承の処理がかなり楽になっていると思います。 テキトーに場当たりで単純にnewを使って継承していたりすると

「親クラスのコンストラクタに必須な引数があって、それがないとエラーでるから継承できねーよ、しゃーない適当に引数付けとくか」

ということになったりするもんですが(俺はよくなってますw)このライブラリを使えば大丈夫。 また、こんな変な書き方イヤだよ!!って方でも継承の部分はClass.inheritに独立させてあるのでそこだけつかえばオッケーです。

一応動作は確認していますがおかしいところあったら報告していただけるとありがたいです。

comments powered by Disqus