javascript框架之繼承機制(二)

作者: 司徒正美  來源: 博客園  發布時間: 2009-11-08 15:09  閱讀: 1153 次  推薦: 0   原文鏈接   [收藏]  

我們來模仿一下最OO的mootools的繼承機制。它的類都有一個叫做initialize構造方法,這與Java的類都有一個與類名同名的構造方法一樣的道理。只不過,這些叫initialize或init都是借鑒自Prototype,而Prototype那幫人是Ruby出身。為了區別mootools那種污染原生方法的做法,我把類的構造器命名為variant,并且禁止查看構造方法(像瀏覽器禁止查看原生對象的構造方法那樣)。

    var variant = function (options){
      options = options || {};
      var initialize = options.initialize || function(){};
      var klass = initialize ;
      klass.constructor = arguments.callee;
      klass.prototype.constructor = klass;
      klass.toString = function(){//禁止查看構造方法
        return "function variant(){\n    [variant code]\n}"
      }
      return klass;
    };

這是一個非常簡單的工廠方法,用于生產類的。options就是一屬性包,可能裝有我們的類的構造方法,我們要做的是把它提取出來,然后輸送出去。

     function factory(a){
         a.b = b;
         a.c = c;
         return a;
      }

不過,這就有點像倒爺,我們應該像水果商,從果農收購水果回來包裝一番,才賣出去。這包裝就是往要生成的類添加各種原型方法與類方法。我們進一步打造我們的類工廠,讓生產的類擁有繼承能力,也就是把第一部分的makeBridge 加上去。

prototype繼承是通過把子類的原型設置成父類的一個實例來進行繼承的。因此無論是inherit也好,mixin也好,都是往子類的原型添加東西。打個比方,繼承就是把一大堆屬性與方法直接加在子類的原型上,mixin則相當于把一打屬性與方法逐一加到構造函數的原型。不過在上面Tiger類的構造器寫得有點不好,因為name屬性本來父類就有,子類就不用定義一次。如果父類有許多實例屬性,豈不是要寫一大打賦值語句。應該改為

    var Tiger = variant({
      inherit:Animal,
      initialize:function(name,age){
        this.superclass(arguments);//this.name = name
        this.age =age;
      },
      getAge : function(){
        return this.age;
      },
      setAge : function(age){
        this.age = age;
      }
    })

但是這種做法在第三代子類就行不通了,比如我們弄個子類叫IndiaTiger,它比Tiger多出一個類例屬性location。

var IndiaTiger = variant({
        inherit:Tiger,
        initialize:function(name,age,location){
          this.superclass(arguments);
          this.location =location;
        }
      });

當new印度虎實例時就報錯了,除非其父類的構造器沒有用到this.superclass(arguments)。不用說,問題是出自this,它的不確定性總是為我們惹很多麻煩。這里的this總為IndiaTiger 的實例,因此this.superclass總為Tiger ,也因此我們無法實例化Animal 。javascript的實例化過程是,有父類先實例化父類,然后再到下級子類。由于我們無法通過klass.prototype.superclass獲取Animal,我們可以用klass.superclass來試一下。klass為類,而this為實例:

IndiaTiger.superclass.apply(this, arguments);
//this為IndiaTiger 的實例
//arguments為IndiaTiger 構造器的參數對象

但我們不能把前面的IndiaTiger 寫死,因為創建IndiaTiger 這個類時,它還不知自己叫IndiaTiger ,我們可以通過arguments.callee獲取IndiaTiger自身。Tiger的構造相仿。

        var IndiaTiger = variant({
          inherit:Tiger,
          initialize:function(name,age,location){
            arguments.callee.superclass.apply(this, arguments);
            this.location =location;
          }
        });

我們可以把它再抽取出來,這樣每次就不用寫這么長的代碼了。

        function _super(o, args) {//o為子類的實例,agrs為子類構造的arguments對象
            return args.callee.superclass.apply(o, args);
        }

上面的代碼已經假設了,它的構造器總會有inherit這個屬性,但如果沒有豈不是會報錯,另,它還假設了我們的類上面有一個屬性叫superclass,因此我們要在類工廠中做相應的調整。添加或修改如下兩行代碼:

  var superclass = options.inherit || Object;
  klass.superclass = superclass; //類的superclass

不過每次設置新類的構造器時都要添加一行_super(this,arguments)也太麻煩了,最好把它隱藏起來,內部調用。另,把_super方法放到工廠外,顯得太松散,既然也是用來構建類,因此也該把它整合到類工廠中。

      function factory(a){
         var b = function(){
             s();
             a();
             //*****其他方法
         }
         return b
      }

也就是說,我們只要這樣設置類的構造器即可:

        var IndiaTiger = Variant({
          inherit:Tiger,
          initialize:function(name,age,location){
            this.location =location;//★★★★★
          }
        });

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()