|
本文暫時不介紹如何使用extjs的組件響應(yīng)事件,而是介紹Extjs的事件的一些實現(xiàn)原理。整個Extjs框架都是以一種面向?qū)ο蟮姆绞介_發(fā)的,所以理解Javascript中的繼承也很重要。我前面的一篇文章 補點基礎(chǔ):Javascript中的類和閉包 也是為這篇做準(zhǔn)備。另外,博客園內(nèi)還有一個寫的很好的系列 JavaScript繼承詳解. 他主要是根據(jù)Douglas Crockford的兩篇文章寫的。 其實實現(xiàn)繼承的原理都差不多,大家可以參考閱讀。
Extjs實現(xiàn)繼承的函數(shù)是一個很核心的函數(shù)Ext.extend,extend方法有兩個重構(gòu)版本,第一個接受兩個參數(shù),第一個是extend( Function superclass, Object overrides ) ,第二個是extend( Function subclass, Function superclass,Object overrides ) : Function,第二個版本是在subclass的基礎(chǔ)上。superclass就是超類的構(gòu)造函數(shù),overrides是一個對象,里邊的屬性就是要覆蓋父類的屬性。繼承了父類的子類具有父類的prototype中的所有方法。并且子類可以覆蓋父類的方法(override),更進(jìn)一步,子類的每個對象也可以覆蓋父類的方法。其實我覺得這個函數(shù)沒什么作用,修改prototype的效果是等效的,當(dāng)然,extjs的目的肯定是要把prototype這個神奇的東西完全屏蔽起來,使程序員能夠像處理其他語言一樣來處理Javascript。當(dāng)然,即使如此,它的繼承和一般的繼承還是有些不同的,下面先看個例子,準(zhǔn)備好一個Person類
復(fù)制代碼 代碼如下:
Person = function(name) {
this.name = name;
this.fn = function() { alert('I am a person') };
}
Person.prototype.print=function(){ alert('I am a person');}
Person.prototype.showAge = function() { alert('I am older than 0'); }
Person.prototype.showName = function() { alert('Show Name:'+this.name) };
var per = new Person('Tom');
per.showName();子類:Student = function(id) {
this.id = id;
}
Student.prototype.showID = function() { alert(this.id); } //子類的方法
繼承:
Ext.extend(Student, Person);
stu.showName(); !!沒有結(jié)果!stu沒有name的定義stu.fn(); !!沒有結(jié)果 stu.showID(); !!!還是沒有結(jié)果到此我們已經(jīng)發(fā)現(xiàn)了一些不同:在父類的構(gòu)造函數(shù)中的內(nèi)容是不會繼承的,父類的構(gòu)造函數(shù)不會被調(diào)用,子類(prototype中)已有的方法也會丟失!繼續(xù)看下去,將Ext.extend下面的代碼替換成:
復(fù)制代碼 代碼如下:
var stu = new Student('01');
Student.override({ print: function() { alert('I am a student'); } });
stu.override({ print: function() { alert('I am a bad student,but I won/'t affect others'); } });
stu.print();
stu.showAge();
var stu2 = new Student();
stu2.print();
這里的函數(shù)都能夠按預(yù)期輸出,showAge是執(zhí)行的父類的方法,stu.print是執(zhí)行的stu.override中指定的方法,而stu2執(zhí)行的是Student.override中指定的方法。到這里,我們已經(jīng)大概能猜出extend是如何實現(xiàn)的了。下面看它真正的源代碼,這個方法位于Ext.js中,代碼和注釋如下:extend : function(){
復(fù)制代碼 代碼如下:
// inline overrides
var io = function(o){ //注意這個方法的this,僅看這里并不知道這個this是什么,下面這個io會被賦值給sbp.override,也就是子類的prototype
for(var m in o){ //從而每個子類的對象的override都會指向這個方法,如果子類對象調(diào)用了override,那么這個this就是子類的對象了。也就是
this[m] = o[m]; //上面的例子中stu.override表現(xiàn)出來的效果,僅對當(dāng)前對象有效。從這里可以看出,override不僅僅是傳統(tǒng)意義上的覆蓋,完全也可以
} //用來添加新方法。
};
var oc = Object.prototype.constructor;
return function(sb, sp, overrides){
if(Ext.isObject(sp)){ //是在檢測當(dāng)前使用的是哪個版本的重構(gòu)函數(shù)。如果sp實際上是overrides,就做些替換工作,讓變量的實際意義和名稱相符合。
overrides = sp;
sp = sb;
sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);}; //這個沒看懂……
}
var F = function(){},
sbp,
spp = sp.prototype;
F.prototype = spp; //F是父類的一個“干凈”拷貝,所謂干凈,是指它不會把父類中在構(gòu)造函數(shù)內(nèi)部定義的屬性帶過來。 //例如 Person=function() // {this.privateFn=new function{ some code goes here}} //那么這個privateFn對子類是不可見的,所以在構(gòu)造函數(shù)中利用this定義的屬性都相當(dāng)于是類的私有變量。
sbp = sb.prototype = new F(); //將子類的prototype設(shè)置為父類的prototype,繼承的核心步驟。 sbp.constructor=sb; //設(shè)置正確的構(gòu)造函數(shù)指向,見 JavaScript繼承詳解
sb.superclass=spp; //設(shè)置父類
if(spp.constructor == oc){ //沒看懂……,這個是干嘛用的?望高人指點
spp.constructor=sp;
}
sb.override = function(o){ //子類的重寫方法,這個重寫方法是函數(shù)的重寫方法。它修改的是prototype。
Ext.override(sb, o); //見最后。
};
sbp.superclass = sbp.supr = (function(){ //設(shè)置原型的父類
return spp;
});
sbp.override = io; //給子類的prototype提供override方法,這樣單個實體也可以覆蓋,它修改的是實體對象。注意和上面的sb的override區(qū)分。
Ext.override(sb, overrides); //重寫
sb.extend = function(o){return Ext.extend(sb, o);}; //給子類提供extend方法,以實現(xiàn)多重繼承
return sb; //返回子類。
};
}();
下面是Ext.override的代碼,比較明了的,和那個inline override相比,它就是修改的prototype:override :
復(fù)制代碼 代碼如下:
function(origclass, overrides){
if(overrides){
var p = origclass.prototype;
Ext.apply(p, overrides);
if(Ext.isIE && overrides.hasOwnProperty('toString')){ // 這個是什么?IE的特殊點?
p.toString = overrides.toString;
}
}
}
現(xiàn)在就可以開始正式介紹Extjs的事件模型了。和其他語言事件類似,首先要為一個類定義事件,其他語言(例如C#)的事件一般有一個專門的event類型,event類型實際上可以看作是委托的數(shù)組,當(dāng)然委托實際上是函數(shù),添加時間監(jiān)聽器(listener),就是想委托數(shù)組中添加委托(函數(shù)),所謂觸發(fā)事件就是把數(shù)組中的函數(shù)統(tǒng)統(tǒng)執(zhí)行一遍。Javascript也是類似的,只是Javascript的函數(shù)比那些語言強大靈活的多,因此也不需要什么event類型了。Javascript的事件看起來就像一個字符串(它內(nèi)部應(yīng)該也是保留了一個數(shù)組的),可以通過Observale.addEvents方法添加事件,通過Observale.fireEvent觸發(fā)事件,通過Observale.addListner增加事件監(jiān)聽器。下面舉一個沒什么意義卻能說明問題的例子。
復(fù)制代碼 代碼如下:
Odder = function(min, max) {
this.min = min;
this.max = max;
this.addEvents('onFindOdd');
}
Ext.extend(Odder, Ext.util.Observable, { run:
function() {
for (var i = this.min; i < this.max; i++) {
if (i % 2 != 0) {
this.fireEvent('onFindOdd',i);
}
}
}
});
var p = new Odder(4, 8);
p.addListener('onFindOdd',function(n){alert(n);});
p.run();
Odder是這么一個類,它通過一個構(gòu)造函數(shù)傳入一個范圍,然后尋找這個范圍內(nèi)的所有奇數(shù),每找到一個就觸發(fā)一個事件。我給它加一個事件處理程序,把它找到的奇數(shù)alert出來。 要注意,這里的事件處理程序的參數(shù)只能靠程序員自己保持一致,它不像委托那樣強類型。
注意,我沒有采用官網(wǎng)上的例子:
復(fù)制代碼 代碼如下:
Employee = Ext.extend(Ext.util.Observable, {
constructor: function(config){
this.name = config.name;
this.addEvents({
"fired" : true,
"quit" : true
});
// Copy configured listeners into *this* object so that the base class's
// constructor will add them.
this.listeners = config.listeners;
// Call our superclass constructor to complete construction process.
Employee.superclass.constructor.call(config)
}
});This could then be used like this:
var newEmployee = new Employee({
name: employeeName,
listeners: {
quit: function() {
// By default, "this" will be the object that fired the event.
alert(this.name + " has quit!");
}
}
});
我覺得官網(wǎng)上的例子內(nèi)部還有文章,它的重載項中包含了constructor屬性,給人的感覺是是重載了父類的構(gòu)造函數(shù),然后子類就會調(diào)用這個構(gòu)造函數(shù)來創(chuàng)建,其實不是的,它改變了Javascript本身的行為,這個就和我上面標(biāo)注的沒有看懂的那幾句代碼有關(guān)系。下回再討論。
JavaScript技術(shù):Extjs學(xué)習(xí)筆記之八 繼承和事件基礎(chǔ),轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。