|
本文暫時不介紹如何使用extjs的組件響應事件,而是介紹Extjs的事件的一些實現原理。整個Extjs框架都是以一種面向對象的方式開發的,所以理解Javascript中的繼承也很重要。我前面的一篇文章 補點基礎:Javascript中的類和閉包 也是為這篇做準備。另外,博客園內還有一個寫的很好的系列 JavaScript繼承詳解. 他主要是根據Douglas Crockford的兩篇文章寫的。 其實實現繼承的原理都差不多,大家可以參考閱讀。
Extjs實現繼承的函數是一個很核心的函數Ext.extend,extend方法有兩個重構版本,第一個接受兩個參數,第一個是extend( Function superclass, Object overrides ) ,第二個是extend( Function subclass, Function superclass,Object overrides ) : Function,第二個版本是在subclass的基礎上。superclass就是超類的構造函數,overrides是一個對象,里邊的屬性就是要覆蓋父類的屬性。繼承了父類的子類具有父類的prototype中的所有方法。并且子類可以覆蓋父類的方法(override),更進一步,子類的每個對象也可以覆蓋父類的方法。其實我覺得這個函數沒什么作用,修改prototype的效果是等效的,當然,extjs的目的肯定是要把prototype這個神奇的東西完全屏蔽起來,使程序員能夠像處理其他語言一樣來處理Javascript。當然,即使如此,它的繼承和一般的繼承還是有些不同的,下面先看個例子,準備好一個Person類
復制代碼 代碼如下:
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(); !!沒有結果!stu沒有name的定義stu.fn(); !!沒有結果 stu.showID(); !!!還是沒有結果到此我們已經發現了一些不同:在父類的構造函數中的內容是不會繼承的,父類的構造函數不會被調用,子類(prototype中)已有的方法也會丟失!繼續看下去,將Ext.extend下面的代碼替換成:
復制代碼 代碼如下:
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();
這里的函數都能夠按預期輸出,showAge是執行的父類的方法,stu.print是執行的stu.override中指定的方法,而stu2執行的是Student.override中指定的方法。到這里,我們已經大概能猜出extend是如何實現的了。下面看它真正的源代碼,這個方法位于Ext.js中,代碼和注釋如下:extend : function(){
復制代碼 代碼如下:
// inline overrides
var io = function(o){ //注意這個方法的this,僅看這里并不知道這個this是什么,下面這個io會被賦值給sbp.override,也就是子類的prototype
for(var m in o){ //從而每個子類的對象的override都會指向這個方法,如果子類對象調用了override,那么這個this就是子類的對象了。也就是
this[m] = o[m]; //上面的例子中stu.override表現出來的效果,僅對當前對象有效。從這里可以看出,override不僅僅是傳統意義上的覆蓋,完全也可以
} //用來添加新方法。
};
var oc = Object.prototype.constructor;
return function(sb, sp, overrides){
if(Ext.isObject(sp)){ //是在檢測當前使用的是哪個版本的重構函數。如果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是父類的一個“干凈”拷貝,所謂干凈,是指它不會把父類中在構造函數內部定義的屬性帶過來。 //例如 Person=function() // {this.privateFn=new function{ some code goes here}} //那么這個privateFn對子類是不可見的,所以在構造函數中利用this定義的屬性都相當于是類的私有變量。
sbp = sb.prototype = new F(); //將子類的prototype設置為父類的prototype,繼承的核心步驟。 sbp.constructor=sb; //設置正確的構造函數指向,見 JavaScript繼承詳解
sb.superclass=spp; //設置父類
if(spp.constructor == oc){ //沒看懂……,這個是干嘛用的?望高人指點
spp.constructor=sp;
}
sb.override = function(o){ //子類的重寫方法,這個重寫方法是函數的重寫方法。它修改的是prototype。
Ext.override(sb, o); //見最后。
};
sbp.superclass = sbp.supr = (function(){ //設置原型的父類
return spp;
});
sbp.override = io; //給子類的prototype提供override方法,這樣單個實體也可以覆蓋,它修改的是實體對象。注意和上面的sb的override區分。
Ext.override(sb, overrides); //重寫
sb.extend = function(o){return Ext.extend(sb, o);}; //給子類提供extend方法,以實現多重繼承
return sb; //返回子類。
};
}();
下面是Ext.override的代碼,比較明了的,和那個inline override相比,它就是修改的prototype:override :
復制代碼 代碼如下:
function(origclass, overrides){
if(overrides){
var p = origclass.prototype;
Ext.apply(p, overrides);
if(Ext.isIE && overrides.hasOwnProperty('toString')){ // 這個是什么?IE的特殊點?
p.toString = overrides.toString;
}
}
}
現在就可以開始正式介紹Extjs的事件模型了。和其他語言事件類似,首先要為一個類定義事件,其他語言(例如C#)的事件一般有一個專門的event類型,event類型實際上可以看作是委托的數組,當然委托實際上是函數,添加時間監聽器(listener),就是想委托數組中添加委托(函數),所謂觸發事件就是把數組中的函數統統執行一遍。Javascript也是類似的,只是Javascript的函數比那些語言強大靈活的多,因此也不需要什么event類型了。Javascript的事件看起來就像一個字符串(它內部應該也是保留了一個數組的),可以通過Observale.addEvents方法添加事件,通過Observale.fireEvent觸發事件,通過Observale.addListner增加事件監聽器。下面舉一個沒什么意義卻能說明問題的例子。
復制代碼 代碼如下:
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是這么一個類,它通過一個構造函數傳入一個范圍,然后尋找這個范圍內的所有奇數,每找到一個就觸發一個事件。我給它加一個事件處理程序,把它找到的奇數alert出來。 要注意,這里的事件處理程序的參數只能靠程序員自己保持一致,它不像委托那樣強類型。
注意,我沒有采用官網上的例子:
復制代碼 代碼如下:
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!");
}
}
});
我覺得官網上的例子內部還有文章,它的重載項中包含了constructor屬性,給人的感覺是是重載了父類的構造函數,然后子類就會調用這個構造函數來創建,其實不是的,它改變了Javascript本身的行為,這個就和我上面標注的沒有看懂的那幾句代碼有關系。下回再討論。
JavaScript技術:Extjs學習筆記之八 繼承和事件基礎,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。