溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

【cocos2d-x從c++到j(luò)s】05:John Resiq的繼承寫法解析

發(fā)布時(shí)間:2020-06-15 23:56:52 來源:網(wǎng)絡(luò) 閱讀:7909 作者:老G 欄目:游戲開發(fā)

今天,我們來看看John Resiq的繼承寫法Simple JavaScript Inheritance。之前已經(jīng)有很多同行分析過了。這個(gè)寫法在cocos2d-x for js中也被使用,并作了少許改動(dòng)。我嘗試著做一些展開描述。

先貼源碼:

  1. cc.Class = function(){};  
  2. cc.Class.extend = function (prop) { 
  3.     var _super = this.prototype;  
  4.  
  5.     // Instantiate a base class (but only create the instance, 
  6.     // don't run the init constructor) 
  7.     initializing = true
  8.     var prototype = new this(); 
  9.     initializing = false
  10.     fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 
  11.  
  12.     // Copy the properties over onto the new prototype 
  13.     for (var name in prop) { 
  14.         // Check if we're overwriting an existing function 
  15.         prototype[name] = typeof prop[name] == "function" && 
  16.             typeof _super[name] == "function" && fnTest.test(prop[name]) ? 
  17.             (function (name, fn) { 
  18.                 return function () { 
  19.                     var tmp = this._super; 
  20.  
  21.                     // Add a new ._super() method that is the same method 
  22.                     // but on the super-class 
  23.                     this._super = _super[name]; 
  24.  
  25.                     // The method only need to be bound temporarily, so we 
  26.                     // remove it when we're done executing 
  27.                     var ret = fn.apply(this, arguments); 
  28.                     this._super = tmp; 
  29.  
  30.                     return ret; 
  31.                 }; 
  32.             })(name, prop[name]) : 
  33.             prop[name]; 
  34.     } 
  35.  
  36.     // The dummy class constructor 
  37.     function Class() { 
  38.         // All construction is actually done in the init method 
  39.         if (!initializing && this.ctor) 
  40.             this.ctor.apply(this, arguments); 
  41.     } 
  42.  
  43.     // Populate our constructed prototype object 
  44.     Class.prototype = prototype; 
  45.  
  46.     // Enforce the constructor to be what we expect 
  47.     Class.prototype.constructor = Class; 
  48.  
  49.     // And make this class extendable 
  50.     Class.extend = arguments.callee; 
  51.  
  52.     return Class; 
  53. }; 

 

  1. cc.Class = function(){};  

做了一個(gè)全局構(gòu)造函數(shù)Class,這個(gè)不需要什么解釋。

  1. cc.Class.extend = function (prop) {  

prop是一個(gè)對象字面量,這個(gè)對象包含了子類所需要的全部成員變量和成員方法。extend函數(shù)就在內(nèi)部遍歷這個(gè)字面量的屬性,然后將這些屬性綁定到一個(gè)“新的構(gòu)造函數(shù)”(也就是子類的構(gòu)造函數(shù))的原型上。

  1. var _super = this.prototype; 

注意,js里面的這個(gè)this的類型是在調(diào)用時(shí)指定的,那么這個(gè)this實(shí)際上是父類構(gòu)造函數(shù)對象。比如你寫了一個(gè)MyNode繼承自cc.Node。相應(yīng)代碼是:

  1. var MyNode = cc.Node.extend({ 
  2. var _super = this.prototype;
  3. ... 
  4. }); 

那么這個(gè)this就是父類cc.Node。

  1. initializing = true
  2. var prototype = new this(); 
  3. initializing = false

生成父類的對象,用于給子類綁定原型鏈。但要注意,因?yàn)檫@個(gè)時(shí)候,什么實(shí)參都沒有,并不應(yīng)該給父類對象中的屬性進(jìn)行初始化(構(gòu)造器參數(shù)神馬的木有怎么初始化啊喵,這玩意實(shí)際是JS語言設(shè)計(jì)上的失誤造成的)。所以用一個(gè)變量做標(biāo)記,防止在這個(gè)時(shí)候進(jìn)行初始化。相關(guān)代碼在后面就會看到。

  1. fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;  

這玩意看起來很亂,這是一個(gè)正則對象,右邊是一個(gè)?表達(dá)式,中間添加了一些正則代碼。這個(gè)對象的作用是,檢測子類函數(shù)中是否有調(diào)用父類的同名方法“_super()”(這種調(diào)用父類方式是由John Resiq約定的)。但這種檢測需要JS解釋器支持把一個(gè)函數(shù)轉(zhuǎn)換為字符串的功能,有些解釋器是不支持的。所以我們先做一個(gè)檢測,自己造了一個(gè)函數(shù),里面有xyz,然后用正則的test函數(shù)在里面搜索xyz。如果返回true,表示支持函數(shù)轉(zhuǎn)字符串,那么就直接返回/\b_super\b/否則返回/.*/

  1.  for (var name in prop) {  
  2.         prototype[name] = typeof prop[name] == "function" &&  
  3.             typeof _super[name] == "function" && fnTest.test(prop[name]) ?  
  4.             (function (name, fn) {  
  5.                 return function () {  
  6.                     var tmp = this._super;  
  7.                     this._super = _super[name];   
  8.                     var ret = fn.apply(this, arguments);   
  9. this._super = tmp;
  10.                     return ret;  
  11.                 };  
  12.             })(name, prop[name]) :  
  13.             prop[name];  
  14. }  

現(xiàn)在重頭戲來了,在這個(gè)地方我們要把傳進(jìn)來的那個(gè)字面量prop的屬性全都綁定到原型上。這地方又他喵的是一個(gè)?表達(dá)式,JR實(shí)在太喜歡用這玩意了。首先,forin把屬性拿出來。然后,因?yàn)槲覀兲砑拥墓δ苁?ldquo;實(shí)現(xiàn)像c++那樣通過子類來調(diào)用父類的同名函數(shù)”,那么需要檢測父類和子類中是否都有這兩個(gè)同名函數(shù)。用的是這段代碼:

  1. typeof prop[name] == "function" && typeof _super[name] == "function" 

然后,我們還要檢測,子類函數(shù)中是否真的使用了_super去調(diào)用了同名的父類函數(shù)。這個(gè)時(shí)候,之前的正則對象fnTest出場。繼續(xù)之前的話題,如果解釋器支持函數(shù)轉(zhuǎn)字符串,那么fnTest.test(prop[name])可以正常檢測,邏輯正常進(jìn)行;如果不支持,那么fnTest.test(prop[name])始終返回true。

這玩意什么用處,這是一個(gè)優(yōu)化,如果子類函數(shù)真的調(diào)父類函數(shù)了,就做一個(gè)特殊的綁定操作(這個(gè)操作我們后面馬上講),如果子類函數(shù)沒有調(diào)父類函數(shù),那么就正常綁定。如果沒法判斷是否調(diào)用了(解釋器不支持函數(shù)轉(zhuǎn)字符串),直接按照調(diào)用了那種情況來處理。雖然損失一些性能,但是可以保證不出問題。

  1. (function (name, fn) {   
  2.     return function () {   
  3.         var tmp = this._super;   
  4.         this._super = _super[name];    
  5.         var ret = fn.apply(this, arguments);    
  6.         this._super = tmp; 
  7.         return ret;   
  8.     };   
  9. })(name, prop[name]) 

繼續(xù),上面的就是我們說的那個(gè)特殊的綁定操作。在這里,我們做了一個(gè)閉包,這里面的this是子類對象,跟之前的那個(gè)this不一樣哦。我們利用閉包的特性,保存了一個(gè)_super,這個(gè)_super被綁定了父類的同名函數(shù)_super[name]。然后我們使用fn.apply(this, arguments)調(diào)用子類函數(shù),并保存返回值。因?yàn)檫@是一個(gè)閉包,所以根據(jù)語法,我們可以在fn的實(shí)現(xiàn)中調(diào)用_super函數(shù)。

  1. function Class() {  
  2.    if (!initializing && this.ctor)  
  3.         this.ctor.apply(this, arguments);  
  4. }  
  5.  
  6. Class.prototype = prototype;   
  7. Class.prototype.constructor = Class;   
  8. Class.extend = arguments.callee;  

生成一個(gè)Class構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)作為這個(gè)大匿名函數(shù)的返回值使用。然后里面就是之前說的,初始化保護(hù),防止在綁定原型鏈的時(shí)候初始化。注意后面那個(gè)玩意ctor,在cocos2d-x for js中,真正的初始化是二段構(gòu)造的那個(gè)init,而不是ctor。在cocos2d-x for js的實(shí)現(xiàn)中ctor里面會調(diào)用一個(gè)函數(shù)cc.associateWithNative(this, 父類),這個(gè)函數(shù)負(fù)責(zé)后臺生成一個(gè)c++對象,然后把c++對象和js對象綁定到一起。

剩下的是例行公事:綁定子類的原型,修正子類的構(gòu)造器指向它自己,給子類添加一個(gè)同樣的extend方法。

最后把這個(gè)完成的構(gòu)造函數(shù)返回出來。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI