javascript架構之繼承機制2

來源:互聯網
上載者:User

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

<br /> var variant = function (options){<br /> options = options || {};<br /> var initialize = options.initialize || function(){};<br /> var klass = initialize ;<br /> klass.constructor = arguments.callee;<br /> klass.prototype.constructor = klass;<br /> klass.toString = function(){//禁止查看構造方法<br /> return "function variant(){\n [variant code]\n}"<br /> }<br /> return klass;<br /> };</p><p> var Person = variant({initialize:function(age){<br /> this.age = age;<br /> }});<br /> alert(Person)//看不到構造方法的實現<br /> var p = new Person(3);<br /> alert(p)<br /> alert(p.age);<br /> alert(p.constructor);//看不到構造方法的實現<br /> var P = variant({})<br /> var a = new P;<br /> alert(a);<br />

運行代碼

    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;    };

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

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

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

<br /> var variant = function (options){<br /> options = options || {};<br /> var initialize = options.initialize || function(){};<br /> var superclass = options.inherit;<br /> delete options.initialize;<br /> delete options.inherit;<br /> var klass = initialize ;<br /> if(superclass){//如果是通過繼承得來的屬性<br /> var bridge = function() {};<br /> bridge.prototype = superclass.prototype;<br /> klass.prototype = new bridge;<br /> klass.prototype.superclass = superclass;<br /> }<br /> for(var i in options){//mixin<br /> klass.prototype[i] = options[i]<br /> }<br /> klass.constructor = arguments.callee;//類的靜態屬性<br /> klass.prototype.constructor = klass;//真正用來建立執行個體的</p><p> klass.toString = function(){<br /> return "function variant(){\n [variant code]\n}"<br /> }<br /> return klass;<br /> };<br /> var Animal = variant({<br /> initialize:function(name){<br /> this.name = name;<br /> },<br /> getName:function(){<br /> return "這是" +this.name;<br /> }<br /> });<br /> var a = new Animal("動物");<br /> alert(a.name)<br /> alert(a.getName())<br /> var Tiger = variant({<br /> inherit:Animal,<br /> initialize:function(name,age){<br /> this.name = name;<br /> this.age =age;<br /> },<br /> getAge : function(){<br /> return this.age;<br /> },<br /> setAge : function(age){<br /> this.age = age;<br /> }<br /> })<br /> var t = new Tiger("老虎",10);<br /> alert(t.age)<br /> alert(t.getName())<br /> t.setAge(11);<br /> alert(t.getAge());<br /> // alert(a.getAge());動物執行個體並沒有此方法<br />

運行代碼

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;        }      });

<br /> var variant = function (options){<br /> options = options || {};<br /> var initialize = options.initialize || function(){};<br /> var superclass = options.inherit;<br /> delete options.initialize;<br /> delete options.inherit;<br /> var klass = initialize ;<br /> if(superclass){//如果是通過繼承得來的屬性<br /> var bridge = function() {};<br /> bridge.prototype = superclass.prototype;<br /> klass.prototype = new bridge;<br /> klass.prototype.superclass = superclass;<br /> }<br /> for(var i in options){//mixin<br /> klass.prototype[i] = options[i]<br /> }<br /> klass.constructor = arguments.callee;//類的靜態屬性<br /> klass.prototype.constructor = klass;//真正用來建立執行個體的</p><p> klass.toString = function(){<br /> return "function variant(){\n [variant code]\n}"<br /> }<br /> return klass;<br /> };<br /> var Animal = variant({<br /> initialize:function(name){<br /> this.name = name;<br /> },<br /> getName:function(){<br /> return "這是" +this.name;<br /> }<br /> });<br /> var Tiger = variant({<br /> inherit:Animal,<br /> initialize:function(name,age){<br /> this.superclass(arguments)<br /> this.age =age;<br /> },<br /> getAge : function(){<br /> return this.age;<br /> },<br /> setAge : function(age){<br /> this.age = age;<br /> }<br /> });<br /> var IndiaTiger = variant({<br /> inherit:Tiger,<br /> initialize:function(name,age,location){<br /> this.superclass(arguments);<br /> this.location =location;<br /> }<br /> });<br /> try{<br /> var i = new IndiaTiger("印度虎",2,"印度");<br /> alert(i.getName());<br /> i.setAge(3);<br /> alert(i.getAge());<br /> alert(i.location);<br /> }catch(e){<br /> alert("報錯了");<br /> alert(e);<br /> }<br />

運行代碼

當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

<br /> function _super(o, args) {//o為子類的執行個體,agrs為子類構造的arguments對象<br /> return args.callee.superclass.apply(o, args);<br /> }<br /> var variant = function (options){<br /> options = options || {};<br /> var initialize = options.initialize || function(){};<br /> var superclass = options.inherit || Object;<br /> delete options.initialize;<br /> delete options.inherit;<br /> var klass = initialize ;<br /> var bridge = function() {};<br /> bridge.prototype = superclass.prototype;<br /> klass.prototype = new bridge;<br /> klass.prototype.superclass = superclass;//執行個體的superclass<br /> for(var i in options){//mixin<br /> klass.prototype[i] = options[i]<br /> }<br /> klass.constructor = arguments.callee;//類的靜態屬性<br /> klass.prototype.constructor = klass;//真正用來建立執行個體的<br /> klass.superclass = superclass; //類的superclass<br /> // klass.toString = function(){<br /> // return "function variant(){\n [variant code]\n}"<br /> // }<br /> return klass;<br /> };<br /> var Animal = variant({<br /> initialize:function(name){<br /> this.name = name;<br /> },<br /> getName:function(){<br /> return "這是" +this.name;<br /> }<br /> });<br /> var Tiger = variant({<br /> inherit:Animal,<br /> initialize:function(name,age){<br /> _super(this,arguments)<br /> this.age =age;<br /> },<br /> getAge : function(){<br /> return this.age;<br /> },<br /> setAge : function(age){<br /> this.age = age;<br /> }<br /> });<br /> var IndiaTiger = variant({<br /> inherit:Tiger,<br /> initialize:function(name,age,location){<br /> _super(this,arguments);<br /> this.location =location;<br /> }<br /> });<br /> var i = new IndiaTiger("印度虎",2,"印度");<br /> alert(i.getName());<br /> i.setAge(3);<br /> alert(i.getAge());<br /> alert(i.location);<br /> alert(i.superclass)//我們暫時撤去toString方法,它將會彈出其父類的構造器initialize</p><p>

運行代碼

不過每次設定新類的構造器時都要添加一行_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;//★★★★★          }        });

<br /><!doctype html><br /><html dir="ltr" lang="zh-CN"><br /> <head><br /> <meta charset="utf-8"/><br /> <title>類</title></p><p> <script type="text/javascript" charset="utf-8"><br /> window.onload = function(){</p><p> var Variant = function (options){<br /> options = options || {};<br /> var initialize = options.initialize || function(){};<br /> var superclass = options.inherit || Object;<br /> var klassname = options.klassname ;<br /> delete options.initialize;<br /> delete options.inherit;<br /> var klass = function() {<br /> superclass.apply(this, arguments);<br /> initialize.apply(this, arguments)<br /> };<br /> var bridge = function() {};//繼承父類<br /> bridge.prototype = superclass.prototype;<br /> klass.prototype = new bridge;<br /> klass.prototype.superclass = superclass;//執行個體的superclass<br /> klass.superclass = superclass; //類的superclass,用於建立父類的執行個體</p><p> for(var i in options){//mixin<br /> klass.prototype[i] = options[i]<br /> }</p><p> klass.constructor = arguments.callee;//類的靜態屬性<br /> klass.prototype.constructor = klass;//用於建立當前類的執行個體</p><p> var getKlassContext = function(c) {<br /> var e = c.toString().replace(/[\s\?]/g,"");<br /> getKlassContext = function() {<br /> return e;<br /> };<br /> return getKlassContext();<br /> };<br /> var getKlassName = function(search,context){<br /> var search = search.toString().replace(/[\s\?]/g,""),<br /> last = search.length >= 50 ? 50 :search.length;<br /> search = search.substring(0,last);<br /> var end = context.indexOf(search),start = end-100;<br /> start = start < 0 ? 0 :start;<br /> var str = context.substring(start,end);<br /> str = str.match(/var(\w+)\=Variant/);<br /> return (str && str[1]) ? str[1] :"Object";<br /> };<br /> if(!klassname){<br /> context = getKlassContext(arguments.callee.caller);<br /> klassname = getKlassName(initialize,context);<br /> if(klassname == "Object"){<br /> throw Error("如果沒有klassname就必須顯式設定initialize");<br /> }<br /> }<br /> klass.klassname = klassname;<br /> klass.prototype.klassname = klassname;<br /> klass.toString = function(){<br /> return ("function "+klassname+"(){\n [variant code]\n}");<br /> }</p><p> return klass;<br /> };</p><p> var Animal = Variant({<br /> initialize:function(name){<br /> this.name = name;<br /> },<br /> getName:function(){<br /> return "這是" +this.name;<br /> }<br /> });<br /> var Tiger = Variant({<br /> inherit:Animal,<br /> initialize:function(name,age){<br /> this.age =age;<br /> },<br /> getAge : function(){<br /> return this.age;<br /> },<br /> setAge : function(age){<br /> this.age = age;<br /> }<br /> });<br /> var IndiaTiger = Variant({<br /> inherit:Tiger,<br /> initialize:function(name,age,location){<br /> this.location =location;<br /> }<br /> });</p><p> var i = new IndiaTiger("印度虎",2,"印度");<br /> alert(i.klassname);<br /> alert(i.getName());<br /> i.setAge(3);<br /> alert(i.getAge());<br /> alert(IndiaTiger);<br /> alert(Tiger);<br /> }</p><p> </script><br /> </head><br /> <body><br /> <pre><br /> var Animal = Variant({<br /> initialize:function(name){<br /> this.name = name;<br /> },<br /> getName:function(){<br /> return "這是" +this.name;<br /> }<br /> });<br /> var Tiger = Variant({<br /> inherit:Animal,<br /> initialize:function(name,age){<br /> this.age =age;<br /> },<br /> getAge : function(){<br /> return this.age;<br /> },<br /> setAge : function(age){<br /> this.age = age;<br /> }<br /> });<br /> var IndiaTiger = Variant({<br /> inherit:Tiger,<br /> initialize:function(name,age,location){<br /> this.location =location;<br /> }<br /> });</p><p> var i = new IndiaTiger("印度虎",2,"印度");<br /> alert(i.klassname);<br /> alert(i.getName());<br /> i.setAge(3);<br /> alert(i.getAge());<br /> alert(IndiaTiger);<br /> alert(Tiger);<br /> </pre><br /> </body><br /></html><br />

運行代碼

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.