qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問(wèn) http://qaseven.github.io/

          從Java的角度理解Ext的extend

           在Java中,我們?cè)趯?shí)現(xiàn)繼承的時(shí)候存在下面幾個(gè)事實(shí):

            1、準(zhǔn)備兩個(gè)類(lèi),他們用extends關(guān)鍵字鏈接起來(lái)

            2、如果超類(lèi)沒(méi)有默認(rèn)構(gòu)造函數(shù),需要在子類(lèi)構(gòu)造函數(shù)中顯式的super并傳參,如果都是默認(rèn)構(gòu)造函數(shù)也可以super,不super虛擬機(jī)是自動(dòng)的

            3、子類(lèi)可追加,覆蓋,重載方法,子類(lèi)可以有自己的私有屬性,他們?cè)谧宇?lèi)構(gòu)造函數(shù)中被構(gòu)造

            4、字段是數(shù)據(jù),方法在代碼區(qū),和類(lèi)建立方法表,同一個(gè)類(lèi)的對(duì)象有自己的數(shù)據(jù)但是共享方法代碼

            比如有兩個(gè)類(lèi),Plane和Space,Plane表示平面,Space表示空間,Space是Plane的子類(lèi),在java中

          1. /** 
          2.  * 根據(jù)字段數(shù)量分配內(nèi)存塊 
          3.  * 實(shí)例化的時(shí)候虛擬機(jī)調(diào)用Plane.Plane方法把這個(gè)內(nèi)存塊作為this和構(gòu)造參數(shù)傳進(jìn)去,初始化完數(shù)據(jù)字段。 
          4.  * 建立方法表映射 
          5.  */ 
          6. class Plane {  
          7.     protected int x;  
          8.     protected int y;  
          9.     Plane(int x, int y) {  
          10.         this.x = x;  
          11.         this.y = y;  
          12.     }  
          13.     public void XY() {  
          14.         System.out.println(x * y);  
          15.     }  
          16. }  
          17. /** 
          18.  * 自動(dòng)擁有了超類(lèi)的行為,但是超類(lèi)的屬性需要超類(lèi)去構(gòu)造 
          19.  * 子類(lèi)可構(gòu)造自己的屬性,添加自己的方法,覆蓋超類(lèi)的方法 
          20.  * <p/> 
          21.  * 按照繼承結(jié)構(gòu)的所有字段分配內(nèi)存塊,調(diào)用Space.Space將這個(gè)內(nèi)存塊作為this和參數(shù)一起傳進(jìn)去 
          22.  * 把超類(lèi)的那部分給超類(lèi),然后自己初始化自己的。 
          23.  * <p/> 
          24.  * 建立方法表 
          25.  */ 
          26. class Space extends Plane {  
          27.     private int z;  
          28.     Space(int x, int y, int z) {  
          29.         super(x, y);  
          30.         this.z = z;  
          31.     }  
          32.     public void XYZ() {  
          33.         System.out.println(x * y * z);  
          34.     }  
          35. }  
          36. public class Test {  
          37.     public static void main(String[] args) {  
          38.         Plane plane = new Plane(2,3);  
          39.         plane.XY();  
          40.         Space space = new Space(234);  
          41.         space.XYZ();  
          42.     }  
          43. }

            那么在js中也一樣,區(qū)別是代碼要放到構(gòu)造函數(shù)(可以理解為Java中的類(lèi))的原型上,原型是放置方法和不變屬性的理想場(chǎng)所,原型是一個(gè)對(duì)象,它和普通對(duì)象唯一不同的就是他有一個(gè)constructor屬性指向它所依附的構(gòu)造器,在java中子類(lèi)查找屬性和方法是通過(guò)虛擬機(jī)來(lái)完成,但是在js中需要通過(guò)原型鏈來(lái)完成。也就是說(shuō)繼承關(guān)系對(duì)程序員是不透明的,需要了解這個(gè)原型機(jī)制,原型機(jī)制上存在兩條鏈,一是原型鏈,二是構(gòu)造函數(shù)鏈。

            仿照上面java的代碼,我們可以完成js的寫(xiě)法,如下:

          1. var Plane = function(x, y) {  
          2.     this.x = x;  
          3.     this.y = y;  
          4. };  
          5. Plane.prototype.XY = function() {  
          6.     alert(this.x * this.y);  
          7. };  
          8. var Space = function(x, y, z) {  
          9.     //用this調(diào)用超類(lèi)構(gòu)造函數(shù),沒(méi)有java的super自動(dòng)調(diào)用,所以要手動(dòng)調(diào)用 
          10.     Plane.call(this, x, y);  
          11.     //Space.superclass.constructor.call(this, x, y); 可以用一個(gè)統(tǒng)一的語(yǔ)法 
          12.     //構(gòu)造自己的數(shù)據(jù) 
          13.     this.z = z;  
          14. };  
          15. Space.prototype.XYZ = function() {  
          16.     alert(this.x * this.y * this.z);  
          17. }

            JS中函數(shù)的this指函數(shù)的調(diào)用者,不管是java還是js,this都可理解為新分配的那段容納對(duì)象的內(nèi)存。在java中通過(guò)SpaceextendsPlane,虛擬機(jī)就維護(hù)好了他們的繼承關(guān)系以完成繼承關(guān)系的自動(dòng)查找,但是在js中需要我們手動(dòng)的處理,在這個(gè)時(shí)候Space是調(diào)用不到XY這個(gè)方法的,因?yàn)樗麄儧](méi)有在原型鏈上。我們可以開(kāi)發(fā)一個(gè)函數(shù)來(lái)模擬java的關(guān)鍵字extends,比如這個(gè)函數(shù)叫做extend,通過(guò)執(zhí)行extend(Plane,Space)完成原型鏈的組裝。

           那么extend怎么實(shí)現(xiàn)呢?首先要明白原型鏈,子類(lèi)和父類(lèi)在原型鏈上的關(guān)系是Space.prototype._proto_==Plane.prototype,如果你理解不了,那就看String這個(gè)類(lèi)吧,String.prototype._proto_==Object.prototype,即String的原型會(huì)鏈接到Object的原型上,鏈接是通過(guò)_proto_這個(gè)屬性來(lái)完成的。_proto_是一個(gè)只讀的屬性,只能通過(guò)構(gòu)造函數(shù)寫(xiě)入,所以String是Object的子類(lèi)。

            現(xiàn)在Plane的prototype._proto_等于Object,Space的prototype._proto_也等于Object,我們要在extend函數(shù)變換這個(gè)關(guān)系,即完成Space.prototype._proto_==Plane.prototype,我們知道一個(gè)對(duì)象的_proto_要指向某個(gè)構(gòu)造函數(shù)的原型,需要讓這個(gè)對(duì)象由那個(gè)構(gòu)造函數(shù)構(gòu)造,那么我們只需要讓Space.prototype=newPlane()就可以了,這個(gè)時(shí)候Space.prototype._proto_==Plane.prototype,而不再指向Object,原型還有一個(gè)屬性constructor指向原型所在的構(gòu)造器,由于Space.prototype剛被Plane創(chuàng)建出來(lái),還沒(méi)有這個(gè)屬性,我們要手動(dòng)賦值上去,代碼是Space.prototype.constructor=Space。這樣extend的責(zé)任就完成了。

            但是這里有兩個(gè)問(wèn)題:

            1、由于Space的原型在extend中被替換了,那么它原有的方法就沒(méi)有了。

            2、Space的原型是Plane構(gòu)造的,雖然做到了Space.prototype._proto_==Plane.prototype,但是Plane也在原型上寫(xiě)入了x,y這兩個(gè)垃圾數(shù)據(jù),他們都是undefined,沒(méi)有意義,所以要手動(dòng)刪除掉,這樣extend這個(gè)方法就不能通用了。

            首先解決第一個(gè)問(wèn)題,我們要變化一點(diǎn)思路,利用js中函數(shù)也是數(shù)據(jù)的特性,我們把Space的那些方法拷貝到一個(gè)對(duì)象中,比如

          1. var sbm= { XYZ  : function() {  
          2.     alert(this.x * this.y * this.z);  
          3. }  
          4.  };

            把這個(gè)sbm也傳遞給extend,extend在替換完原型后將sbm上的所有方法復(fù)制到Space的原型上即可,sbm是一個(gè)對(duì)象直接量,用json語(yǔ)法。現(xiàn)在的extend就變?yōu)榱巳齻€(gè)參數(shù),即extend(sb,sp,sbm),sb是子類(lèi),sp是超類(lèi),sbm是子類(lèi)要放到原型上的方法。

            對(duì)于第二個(gè)問(wèn)題,本質(zhì)原因是Plane這個(gè)函數(shù)要完成一些數(shù)據(jù)初始化,它是超類(lèi),我們不能控制,我們只關(guān)心Plane的原型而不關(guān)心它構(gòu)造什么數(shù)據(jù),所以我們可以把它的原型單獨(dú)拿出來(lái),再定義一個(gè)干凈的函數(shù),這個(gè)函數(shù)不做任何事,將這個(gè)干凈函數(shù)的原型設(shè)置為Plane的原型,再用這個(gè)干凈函數(shù)構(gòu)造一個(gè)對(duì)象,這樣出來(lái)的對(duì)象就是是干凈的,也完成了_proto_指向了Plane.prototype,完美!有了這兩個(gè)方法,我們就可以開(kāi)始實(shí)現(xiàn)這個(gè)extend,代碼如下:

          1. var extend = function(sb, sp, sbm) {  
          2.     var F = function() {  
          3.     },sbp,spp = sp.prototype;  
          4.  
          5.     F.prototype = spp;  
          6.  
          7.     //用干凈函數(shù)嫁接得到子類(lèi)原型  
          8.     sbp = sb.prototype = new F();   
          9.  
          10.     sbp.constructor = sb; //然后指定一個(gè)constructor指回子類(lèi)   
          11.  
          12.          //把sbm的上的屬性拷貝到子類(lèi)的原型上  
          13.         for (var p in sbm) {  
          14.         sbp[p] = sbm[p];  
          15.     }  
          16. };

            那么完成Space繼承Plane的代碼如下:

          1. extend(Space, Plane, {  
          2.             XYZ : function() {  
          3.                 alert(this.x * this.y * this.z);  
          4.             }  
          5.         });  
          6.  
          7. var spaceObject = new Space(234);  
          8.  
          9. spaceObject.XY();//成功調(diào)用超類(lèi)方法  
          10. spaceObject.XYZ();

            OK,到了這里,我們基本上就完成任務(wù)了,完全從java的方向搞定的。我們現(xiàn)在利用js的特性來(lái)優(yōu)化,讓使用extend更加簡(jiǎn)單。

          我們說(shuō)在java中必須寫(xiě)兩個(gè)類(lèi),每個(gè)類(lèi)都寫(xiě)自己的字段 ,構(gòu)造函數(shù),方法等,在我們實(shí)現(xiàn)的extend函數(shù)中也確實(shí)把子類(lèi),父類(lèi)都傳遞了進(jìn)來(lái),但是我們多了一個(gè)參數(shù),那就是子類(lèi)的方法集合即sbm,第一個(gè)參數(shù)sb本身也是函數(shù),那是不是可以將這個(gè)函數(shù)也放進(jìn)sbm傳進(jìn)來(lái)呢?這樣extend就變?yōu)閮蓚€(gè)參數(shù),即extend(sp,sbm),現(xiàn)在extend返回一個(gè)函數(shù),返回的這個(gè)函數(shù)就是sp的子類(lèi),這是完全可行的,我們叫做extend2吧。

          1. var extend2 = function(sp, sbm) {  
          2.     var sb = sbm.constructor;  
          3.     //如果說(shuō)沒(méi)有顯式的構(gòu)造函數(shù),那么子類(lèi)就是直接調(diào)用超類(lèi)構(gòu)造函數(shù)  
          4.     if (sb == Object) {  
          5.         sb = function() {  
          6.             sp.apply(this, arguments);  
          7.         };  
          8.     }  
          9.     extend(sb, sp, sbm);  
          10.     return sb;  
          11. }

            我們說(shuō)要把子類(lèi)的構(gòu)造函數(shù)放到sbm上,放上去的key叫做constructor,就表示構(gòu)造器,js中每一個(gè)對(duì)象都一個(gè)constructor屬性,它指向構(gòu)造了這個(gè)對(duì)象構(gòu)造函數(shù)。sbm本來(lái)是個(gè)Object對(duì)象,它的constructor就指向Object,這個(gè)constructor是在sbm關(guān)聯(lián)的那個(gè)原型上的,現(xiàn)在我們?cè)趕bm上設(shè)置某個(gè)子類(lèi)的構(gòu)造函數(shù),這個(gè)時(shí)候表示sbm有個(gè)自己的constructor。

            現(xiàn)在我們?cè)趀xtend2中要做的事情就是提取出構(gòu)造函數(shù),然后還原為三個(gè)參數(shù)去調(diào)用之前的extend,在java中我們的子類(lèi)是可以不用構(gòu)造器的,只要父類(lèi)也有默認(rèn)的構(gòu)造器,那么在這里一樣,sbm可能不包含constructor,那么我們需要做一個(gè)函數(shù),它調(diào)用父類(lèi)的構(gòu)造函數(shù),在java中這種情況過(guò)程是自動(dòng)的。所以當(dāng)sbm.constructor為Object的時(shí)候表示sbm沒(méi)有指定構(gòu)造函數(shù),這個(gè)時(shí)候?qū)?/p>

          1. sb = function() {  
          2.             sp.apply(this, arguments);  
          3.         };

            即調(diào)用父類(lèi)構(gòu)造函數(shù)。這樣將sb,sp,sbm傳遞給extend就可以了。

            這個(gè)時(shí)候我們新的繼承語(yǔ)法如下:

          1. var NewSpace = extend2(Plane, {  
          2.             constructor : function(x, y, z) {  
          3.                 Plane.call(this, x, y);  
          4.                 this.z = z;  
          5.             },  
          6.             XYZ : function() {  
          7.                 alert(this.x * this.y * this.z);  
          8.             }  
          9.         });  
          10.  
          11. var newObject = new NewSpace(345);  
          12. newObject.XY();  
          13. newObject.XYZ();

            和prototype.js和mootolls的實(shí)現(xiàn)比較,大同小異

          1. // properties are directly passed to `create` method  
          2. var Person = Class.create({  
          3.   initialize: function(name) {  
          4.     this.name = name;  
          5.   },  
          6.   say: function(message) {  
          7.     return this.name + ': ' + message;  
          8.   }  
          9. });

          1. var Animal = new Class({  
          2.                 initialize: function(age) {  
          3.                     this.age = age;  
          4.                 },  
          5.                 say : function() {  
          6.                     alert(this.age);  
          7.                 }  
          8.             });  
          9.  
          10.     var Cat = new Class({  
          11.                 Extends: Animal,  
          12.                 initialize: function(name, age) {  
          13.                     this.parent(age); // calls initalize method of Animal class  
          14.                     this.name = name;  
          15.                 }  
          16.             });

            到了這里其實(shí)已經(jīng)差不多了,但是細(xì)心的讀者會(huì)發(fā)現(xiàn),我們?cè)趀xtend中會(huì)把sbm的所有屬性拷貝到子類(lèi)的原型上,這里豈不是就要把constructor也拷貝到原型上?如果sbm包含了這個(gè)constructor其實(shí)就無(wú)所謂,因?yàn)樽宇?lèi)的原型的constructor本來(lái)就是需要指向這個(gè)構(gòu)造函數(shù)的,但是sbm上沒(méi)有constructor那豈不是要把Object拷貝到子類(lèi)原型上,答案是不會(huì)的,我們?cè)诳截惖臅r(shí)候用的for in循環(huán)是迭代不出默認(rèn)的那個(gè)constructor的。

           現(xiàn)在我們來(lái)看看Ext.extend,應(yīng)該完全沒(méi)有問(wèn)題了。我們用了兩個(gè)方法extend,extend2,Ext把它合并為了一個(gè)方法Ext.extend,所以它會(huì)判斷傳進(jìn)來(lái)的參數(shù)然后進(jìn)行變換,這樣Ext.extend就支持兩個(gè)參數(shù)和三個(gè)參數(shù)進(jìn)行調(diào)用。對(duì)于前面用到拷貝屬性,Ext做了一個(gè)工具函數(shù)叫做Ext.apply,對(duì)于將一個(gè)對(duì)象的屬性拷貝到一個(gè)類(lèi)的原型上,Ext做了一個(gè)工具類(lèi)叫做Ext.override。

          1. Ext.extend = function() {  
          2.     // inline overrides 把傳入的對(duì)象屬性復(fù)制到到this中  
          3.     var io = function(o) {  
          4.         for (var m in o) {  
          5.             this[m] = o[m];  
          6.         }  
          7.     };  
          8.     //oc其實(shí)就是Object函數(shù)  
          9.     var oc = Object.prototype.constructor;  
          10.  
          11.     return function(sb, sp, overrides) {  
          12.         //如果第二個(gè)參數(shù)是個(gè)對(duì)象而不是類(lèi),那么是用兩個(gè)參數(shù)調(diào)用的,第一個(gè)參數(shù)是父類(lèi),第二個(gè)參數(shù)是對(duì)象  
          13.         if (typeof sp == 'object') {  
          14.             overrides = sp;  //將第三個(gè)參數(shù)換為對(duì)象  
          15.             sp = sb; //把第一個(gè)參數(shù)賦值第二個(gè)當(dāng)成父類(lèi)  
          16.             sb = overrides.constructor != oc ? overrides.constructor : function() {  
          17.                 sp.apply(this, arguments);  
          18.             }; //子類(lèi)這個(gè)構(gòu)造函數(shù)要么是外界傳入的名字為constructor,要么就是直接調(diào)用超類(lèi)構(gòu)造函數(shù)的一個(gè)函數(shù)  
          19.             //傳入的constructor除了構(gòu)造自己還要調(diào)用超類(lèi)的構(gòu)造函數(shù)  
          20.         }  
          21.  
          22.         /**  
          23.          * 繼承的兩種參數(shù)  
          24.          * 1,自己寫(xiě)一個(gè)構(gòu)造函數(shù),初始化一些字段,然后調(diào)用超類(lèi)構(gòu)造函數(shù),再寫(xiě)一個(gè)json對(duì)象,里面是要覆蓋超類(lèi)的方法或者追加的方法  
          25.          *   然后這樣調(diào)用extend(sub,sup,{over1:f,over2:f,addf:f}),就像java的語(yǔ)法  
          26.          *   SubClass extend SuperClass {  
          27.          *      SubClass(){  
          28.          *        super();  
          29.          *      }  
          30.          *   }  
          31.          *  
          32.          *   2,第一種可以理解為模擬java,但是因?yàn)闃?gòu)造函數(shù)也是數(shù)據(jù),所以完全可以把構(gòu)造函數(shù)也放進(jìn)那個(gè)jdon對(duì)象,只不過(guò)約定好一個(gè)名字  
          33.          *   比如constructor,然后這樣調(diào)用  
          34.          *   extend(sup,{constructor:f,over1:f,over2:f,addf:f})  
          35.          */ 
          36.         var F = function() {  
          37.         },  
          38.                 sbp,  
          39.                 spp = sp.prototype;  
          40.  
          41.         F.prototype = spp;  
          42.         sbp = sb.prototype = new F();  
          43.         //以上用干凈函數(shù)嫁接得到子類(lèi)原型  
          44.  
          45.         sbp.constructor = sb; //然后指定一個(gè)constructor指回子類(lèi),這樣就大工告成  
          46.  
          47.         sb.superclass = spp; //在子類(lèi)上指定一個(gè)靜態(tài)字段指向超類(lèi)原型,這樣在子類(lèi)構(gòu)造函數(shù)中可訪問(wèn)超類(lèi)構(gòu)造函數(shù)sub.superclass.constructor.call(this, config)  
          48.  
          49.         /**  
          50.          * 這段代碼是防御性的,在自己實(shí)現(xiàn)繼承的時(shí)候,可能會(huì)出現(xiàn)原型上的構(gòu)造函數(shù)指向問(wèn)題,所以如果發(fā)現(xiàn)某個(gè)超類(lèi)  
          51.          * 的構(gòu)造函數(shù)是object,要么這個(gè)超類(lèi)卻是Object,要么出現(xiàn)了失誤,所以這里再一次重設(shè)置一下,以防萬(wàn)一,這個(gè)代碼我們?cè)诜治鯡xt的Observable的時(shí)候會(huì)提到的它的作用  
          52.          */ 
          53.         if (spp.constructor == oc) {  
          54.             spp.constructor = sp;  
          55.         }  
          56.  
          57.         //子類(lèi)上方一個(gè)靜態(tài)的重寫(xiě)方法,注意js沒(méi)有重載,可以用來(lái)重寫(xiě)子類(lèi)原型上的函數(shù)  
          58.         sb.override = function(o) {  
          59.             Ext.override(sb, o);  
          60.         };  
          61.  
          62.         //用一個(gè)閉包在子類(lèi)原型上引用一個(gè)超類(lèi)原型,引用的是一個(gè)函數(shù)  
          63.         sbp.superclass = sbp.supr = (function() {  
          64.             return spp;  
          65.         });  
          66.  
          67.         //子類(lèi)原型上放置一個(gè)重寫(xiě)函數(shù),可以用來(lái)覆蓋具體實(shí)例對(duì)象  
          68.         sbp.override = io;  
          69.  
          70.         //在子類(lèi)原型上重寫(xiě)或添加函數(shù)  
          71.         Ext.override(sb, overrides);  
          72.  
          73.         //子類(lèi)上直接放一個(gè)靜態(tài)繼承方法,貌似實(shí)現(xiàn)多繼承  
          74.         sb.extend = function(o) {  
          75.             return Ext.extend(sb, o);  
          76.         };  
          77.  
          78.         return sb;  
          79.     };  
          80. }();

            現(xiàn)在使用Ext的extend來(lái)實(shí)現(xiàn)我們之前的繼承代碼就如下

          1. var Plane = function(o) {  
          2.         this.x = o.x;  
          3.         this.y = o.y;  
          4.     };  
          5.  
          6.  
          7.     Plane.prototype.XY = function() {  
          8.         alert(this.x * this.y);  
          9.     };  
          10.  
          11.     var Space = Ext.extend(Plane, {  
          12.                 constructor : function(o) {  
          13.                     Space.superclass.constructor.call(this, o);  
          14.                     this.z = o.z;  
          15.                 },  
          16.                 XYZ : function() {  
          17.                     alert(this.x * this.y * this.z);  
          18.                 }  
          19.             });  
          20.  
          21.     var space = new Space({ x:2,y:3,z:4});  
          22.  
          23.     space.XY();  
          24.     space.XYZ();

          現(xiàn)在我們來(lái)分析一下Ext中的繼承重頭戲Observable,它位于Ext.util這個(gè)包下,它的意思即是觀察者,使用觀察者模式,EDA模式,UI組件就利用這種基于觀察和事件的機(jī)制進(jìn)行通信和渲染。

            所有的UI組件都繼承這個(gè)類(lèi),我們看看它的構(gòu)造函數(shù)

          1. EXTUTIL.Observable = function(){    
          2.     var me = this, e = me.events;  
          3.     if(me.listeners){  
          4.         me.on(me.listeners);  
          5.         delete me.listeners;  
          6.     }  
          7.     me.events = e || {};  
          8. };

            這個(gè)構(gòu)造函數(shù)不需要參數(shù),在java中,這種父類(lèi)的構(gòu)造可以自動(dòng)的調(diào)用默認(rèn)構(gòu)造函數(shù),但是這里要注意,if(me.listeners)依賴了子類(lèi)的構(gòu)造行為,這在面向?qū)ο笤瓌t中似乎是一個(gè)禁忌,但是如果一個(gè)繼承體現(xiàn)完全由一個(gè)團(tuán)隊(duì)維護(hù),他們同時(shí)制定繼承規(guī)則和繼承規(guī)范,這也無(wú)可厚非,這里的listeners可以在子類(lèi)中不提供,可以讓構(gòu)造出來(lái)的對(duì)象自己調(diào)用on方法來(lái)添加監(jiān)聽(tīng)器,同理這里的events,如果子類(lèi)沒(méi)構(gòu)造會(huì)被賦值為一個(gè)空對(duì)象。那么這個(gè)Observable構(gòu)造器做了兩個(gè)事,一個(gè)是看子類(lèi)是否在對(duì)象上放了監(jiān)聽(tīng)器,如果放了,就調(diào)用對(duì)象的on方法進(jìn)行事件和監(jiān)聽(tīng)的綁定,二是看子類(lèi)是否在對(duì)象上放置了events,如果沒(méi)有就把對(duì)象的events屬性設(shè)置為一個(gè)空對(duì)象。也就是說(shuō)子類(lèi)是完全可以不做任何事的,子類(lèi)只負(fù)責(zé)自己的數(shù)據(jù)構(gòu)造和行為覆蓋或追加,events在和監(jiān)聽(tīng)器綁定之后就是一個(gè)Ext.util.Event對(duì)象的容器,見(jiàn)這行代碼: me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);也就是說(shuō),在Ext中,一個(gè)活生生的能夠響應(yīng)事件的對(duì)象有一個(gè)Event容器,它保存了這個(gè)對(duì)象可以響應(yīng)什么事件以及事件被觸發(fā)后被調(diào)用的監(jiān)聽(tīng)器。

            Observable原型上放置的方法都是子類(lèi)繼承的方法,子類(lèi)的對(duì)象就可以在運(yùn)行時(shí)調(diào)用這些方法,如下:

            原型上放置了一個(gè)靜態(tài)變量和一些方法,這些方法都是和事件以及監(jiān)聽(tīng)有關(guān),注意Observable的原型是一個(gè)新的對(duì)象直接量,它的constructor屬性肯定指向的是Object,不是指向的Observable,這豈不是存在bug,我通過(guò)代碼檢測(cè)發(fā)現(xiàn)alert(Ext.util.Observable.prototype.constructor == Ext.util.Observable);的結(jié)果確實(shí)又是true,怎么回事呢?答案就在Ext.extend的那段防御性代碼,大家回過(guò)去看看吧!

            現(xiàn)在我們寫(xiě)一個(gè)繼承Obervable的類(lèi),不過(guò)不是UI組件,而是一個(gè)領(lǐng)域模型,比如論壇帖子,它在被修改之后會(huì)跑出一個(gè)被修改的事件,監(jiān)聽(tīng)器捕獲這個(gè)事件將修改保存到數(shù)據(jù)庫(kù)中,代碼如下:

          1. ForumThread = Ext.extend(Ext.util.Observable, {  
          2.                 constructor: function(config) {  
          3.                     this.name = config.name;  
          4.                     //把監(jiān)聽(tīng)器放進(jìn)超類(lèi)的屬性  
          5.                     this.listeners = config.listeners;  
          6.  
          7.                     this.events = {"change" : true};  
          8.  
          9.                     //給領(lǐng)域模型設(shè)置事件,通過(guò)上面的寫(xiě)法也可以  
          10.  /*                   this.addEvents({  
          11.                                 "change" : true  
          12.                             });*/ 
          13.  
          14.                     //調(diào)用超類(lèi)構(gòu)造超類(lèi)不變量  
          15.                     ForumThread.superclass.constructor.call(this, config)  
          16.                 },  
          17.                 //領(lǐng)域行為,會(huì)觸發(fā)事件  
          18.                 changeName : function(newName) {  
          19.                     alert("原主題名字是:" + this.name);  
          20.                     this.name = newName;  
          21.                     alert("更改后主題名字是:" + this.name);  
          22.                     this.fireEvent("change"this);//觸發(fā)事件  
          23.                 }  
          24.             });  
          25.  
          26.     Ext.onReady(function() {  
          27.         var forumThread = new ForumThread({  
          28.                     name : '關(guān)于將Jdon框架提升為DCI框架的設(shè)想',  
          29.                     //構(gòu)造領(lǐng)域模型時(shí)注入監(jiān)聽(tīng)處理程序  
          30.                     listeners : {  
          31.                         change : function(thread) {  
          32.                             alert('接受到事件,將異步保存新的名字:' + thread.name);  
          33.                         }  
          34.                     }  
          35.                 });  
          36.         //領(lǐng)域行為調(diào)用  
          37.         forumThread.changeName("關(guān)于將Jdon框架提升為DCI框架的設(shè)想,整合JdonMVC");  
          38.     });

            如果事件設(shè)置和監(jiān)聽(tīng)綁定直接在子類(lèi)完成,那么就不必顯式調(diào)超類(lèi)構(gòu)造函數(shù)

          1. ForumThread = Ext.extend(Ext.util.Observable, {  
          2.             constructor: function(config) {  
          3.                 this.name = config.name;  
          4.                 this.events = {"change" : true};  
          5.                 this.on(config.listeners);  
          6.             },  
          7.             //領(lǐng)域行為,會(huì)觸發(fā)事件  
          8.             changeName : function(newName) {  
          9.                 alert("原主題名字是:" + this.name);  
          10.                 this.name = newName;  
          11.                 alert("更改后主題名字是:" + this.name);  
          12.                 this.fireEvent("change"this);//觸發(fā)事件  
          13.             }  
          14.         });

          posted on 2011-12-29 13:26 順其自然EVO 閱讀(203) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2011年12月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類(lèi)

          隨筆檔案

          文章分類(lèi)

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 普陀区| 阳城县| 大足县| 许昌市| 罗甸县| 阳高县| 白沙| 同仁县| 青阳县| 桃园市| 和平县| 夏邑县| 彰化市| 丹东市| 桂东县| 剑河县| 鹤庆县| 赤城县| 宁蒗| 正镶白旗| 济宁市| 江永县| 蒙城县| 磐安县| 浮山县| 台北县| 班戈县| 丽水市| 时尚| 钟山县| 苍山县| 深水埗区| 彰武县| 织金县| 凤阳县| 株洲县| 冀州市| 磴口县| 徐闻县| 余庆县| 宜州市|