第一句
window.undefined = window.undefined;
兼容IE6以下的瀏覽器,有人 解釋過了。
定義Ext全局變量
Ext = {
version : '3.1.0'
};
注意這里沒有使用var,不使用var聲明變量被認為是不好的編程習慣,尤其是函數內聲明變量不寫var更被稱為是無窮無盡的Bug根源。這里則不會,因為變量Ext是該庫唯一的全局變量(命名空間)。其它Ext源碼中的變量聲明都加上了var。
給Ext添加靜態apply方法,該方法是核心方法之一,會用其擴展Ext。
Ext.apply = function(o, c, defaults){
// no "this" reference for friendly out of scope calls
if(defaults){
Ext.apply(o, defaults);
}
if(o && c && typeof c == 'object'){
for(var p in c){
o[p] = c[p];
}
}
return o;
};
該方法有兩種執行方式:
其一,只傳o,c時直接將c上的所有屬性/方法拷貝給o后返回;
其二,defaults也傳時,會將defaults,c上的所有屬性/方法都拷貝給o。這里實現的很巧妙,同時有點繞人。
三個參數都傳,會執行Ext.apply(o, defaults),即自身實現中調用自身。defaults為c,即當傳兩個參數時會直接進行對象拷貝。舉個例子一目了然,在Ext.apply中加上一個輸出語句
Ext.apply = function(o, c, defaults){
// no "this" reference for friendly out of scope calls
if(defaults){
Ext.apply(o, defaults);
}
if(o && c && typeof c == 'object'){
for(var p in c){
alert(p); // 此處是添加的輸出語句
o[p] = c[p];
}
}
return o;
};
var obj = {}, obj1 = {name:'jack'}, obj2 = {age:33};
Ext.apply(obj,obj1,obj2);
會發現先彈出age,再是name。即先拷貝defaults,再是c。
接下來是個自執行的匿名函數,執行完后給Ext上擴充許多實用屬性或方法。先定義了一些局部變量idSeed,瀏覽器判斷之類。idSeed在做Dom緩存時用到。接下來,
if(isIE6){
try{
DOC.execCommand("BackgroundImageCache", false, true);
}catch(e){}
}
這段代碼用來解決IE6下css背景圖不緩存bug,也 有人 解釋過了。
接下就是一個Ext.apply(Ext,{...}),給Ext對象擴展許多實用屬性及方法。
注意 Ext.isStrict 並非判斷html文檔模式為嚴格模式,而是指標准模式,如<!DOCTYPE HTML>聲明會返回true。關於文檔模式猛擊:http://hsivonen.iki.fi/doctype ,國內的 秦歌 翻譯了該篇文章。
Ext.isSecure 判斷采用https或是其它。
Ext.applyIf 設計的很巧妙,它會把對象沒有的屬性和方法拷貝下來,已經有的則不拷貝。Ext.apply 則會覆蓋已有的屬性/方法。
ECMAScript 5已經發布1年多了,添加了一些新的API方法,如Array的indexOf,forEach等方法,部分新版本瀏覽器已經支持這些方法來,但我們想為老的瀏覽器擴展該方法。可能會這樣寫
var proto = Array.prototype;
if(!proto.indexOf){
proto.indexOf = function(){
// ...
}
}
if(!proto.forEach){
proto.forEach = function(){
// ...
}
}
Ext.id方法會為HTMLElement元素隨機生成一個id,默認以"ext-gen"開頭。
接下來是Ext.extend方法,該方法也是核心方法之一,整個ext框架繼承都是以該方法來擴展的。該方法實現依賴於Ext.override,先看override
override : function(origclass, overrides){
if(overrides){
var p = origclass.prototype;
Ext.apply(p, overrides);
if(Ext.isIE && overrides.hasOwnProperty('toString')){
p.toString = overrides.toString;
}
}
}
將對象overrides的所有屬性/方法拷貝到類origclass的原型上。需要注意的是后面的if判斷,IE中for in不能遍歷對象的Object的toSting等方法,因此需要特別處理一下。我測試IE9 beta重寫對象的內置方法如toString后是可用for in遍歷的,見 for in的缺陷 。IE9 beta剛剛發布,不知今后Ext團隊是否會修改此處的判斷。
Ext.extend是js繼承最經典的實現方式了,我曾經模仿其(簡化版)應用在 51ditu 。
extend : function(){
// inline overrides
var io = function(o){
for(var m in o){
this[m] = o[m];
}
};
var oc = Object.prototype.constructor;
return function(sb, sp, overrides){
if(Ext.isObject(sp)){
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;
sbp = sb.prototype = new F();
sbp.constructor=sb;
sb.superclass=spp;
if(spp.constructor == oc){
spp.constructor=sp;
}
sb.override = function(o){
Ext.override(sb, o);
};
sbp.superclass = sbp.supr = (function(){
return spp;
});
sbp.override = io;
Ext.override(sb, overrides);
sb.extend = function(o){return Ext.extend(sb, o);};
return sb;
};
}(),
整體瀏覽,可以看到 Ext.extend 的實現是通過一個匿名函數執行,執行后返回function,這個function才是真正的Ext.extend。
匿名函數中有兩個私有函數io,oc。這種組織代碼的方式非常簡練,通過匿名函數自執行,在匿名函數中你可以做任何復雜的操作,最終的目的返回需要的接口函數或類。
有三個參數,sb、sp、overrides分別代表subClass(子類)、superClass(父類)及覆蓋子類的配置參數。
以下分三種情況討論,第一,二種情況Ext.extend的第二個參數都是對象類型
1, Ext.extend不光是用來實現類繼承的,還可以用來寫類,一年前 討論 過 。
2, 用來擴展Ext庫自身類,這種情況是比較頻繁的
MyGridPanel = Ext.extend(Ext.grid.GridPanel, {
constructor: function(config) {
MyGridPanel.superclass.constructor.apply(this, arguments);
},
yourMethod: function() {
// etc.
}
});
這里以Ext.grid.GridPanel為基礎,生成了一個新類MyGridPanel
3, 真正意義類繼承,即第一,二個參數都是類(function)。1年前也總結過js如何實現 繼承 及 工具函數 。
篇幅已經很長了,重點說下 Ext.apply, Ext.applyif, Ext.override, Ext.extend 的區別 :
Ext.apply, Ext.applyif, Ext.override 都是對對象 進行擴展的方法,Ext.extend則是對類 操作的方法。
Ext.apply 擴展時對已經存在的屬性/方法會被覆蓋掉, 通常用它來擴展普通對象.
Ext.applyif 擴展時不會覆蓋已經存在的屬性/方法,通常用它來擴展核心js,如Array.prototype,String.prototype等。
Ext.override 擴展某一個類的原型,可以覆蓋toString方法。
Ext.extend 用來寫類或繼承,或者說用來擴展類。
