最近準備系統的學習一下Ext的源碼,SO,話很少說,從第一篇開始。html
首先,先看一下Ext.js這個文件的代碼結構:node
var Ext = Ext || {}; // 定義全局變量 Ext._startTime = new Date().getTime(); (function(){ // 定義一些局部變量 var ...... // 核心 Ext.apply = function(){......}; // 初始化 Ext 的一些屬性函數 Ext.apply(Ext, {......}); }()); // 全局閉包 Ext.globalEval = ......
代碼的結構不難,可是仔細看下來,有些細節的地方仍是很值得回味一番。下面具體分析一下我對源碼的理解,水平有限,不足之處還望各位看官指正。閉包
1. 定義局部變量app
var global = this, objectPrototype = Object.prototype, toString = objectPrototype.toString, enumerables = true, enumerablesTest = {toString: 1}, emptyFn = function () {}, ...... for (i in enumerablesTest) { enumerables = null; }
// 看到這裏的時候,有點疑惑,enumerables 不是確定會被置爲 null 嗎 // 下面爲何還須要判斷enunerables? 有蹊蹺..... // 返回去再看一下 enumerablesTest,裏面只有一個屬性 toString, 爲啥恰恰是這樣的一個屬性?? 嗯!想起來了,在IE6下,對象中的toString屬性,不能經過
hasOwnProperty或for...in迭代獲得 // So,這裏其實就是爲了兼容IE6用的。bingo! if (enumerables) { enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; }
2.Ext.applydom
Ext.apply = function(object, config, defaults) { if (defaults) { Ext.apply(object, defaults); } if (object && config && typeof config === 'object') { var i, j, k; // 拷貝對象,不過這裏只是實現了淺拷貝,即若是一個對象中的屬性也是一個Object或者Array,會有引用的問題。 for (i in config) { object[i] = config[i]; } // 兼容IE6 if (enumerables) { for (j = enumerables.length; j--;) { k = enumerables[j]; if (config.hasOwnProperty(k)) { object[k] = config[k]; } } } } return object; };
Ext.apply沒有實現深層拷貝對象的功能,要想了解更多的關於Ext中拷貝對象的內容,能夠狠狠的點擊這裏。ide
3.初始化 Ext 對象函數
3.1學習
Ext.apply(Ext, { ...... // 拷貝對象,不去覆蓋object中的原始屬性。 applyIf: function(object, config) { var property; if (object) { for (property in config) { if (object[property] === undefined) { object[property] = config[property]; } } } return object; }, // array 和 object 的數據迭代 iterate: function(object, fn, scope) { if (Ext.isEmpty(object)) { return; } if (scope === undefined) { scope = object; } // 能夠看到這裏真正起做用的是 Ext.Array.each 和 Ext.Object.each方法。 if (Ext.isIterable(object)) { Ext.Array.each.call(Ext.Array, object, fn, scope); } else { Ext.Object.each.call(Ext.Object, object, fn, scope); } // 複習一下 function.call的用法,第一個參數指定的函數運行過程當中所指代的 this。 } });
3.2this
Ext.apply(Ext, { // 在4.0或以上版本中這個方法已經被Ext.define取代 extend: ...... override: function (target, overrides) { if (target.$isClass) { // 若target是一個 class,則調用該類本身的override方法 target.override(overrides); } else if (typeof target == 'function') { // 若target是一個 function, 則將overrides拷貝到原型鏈上 Ext.apply(target.prototype, overrides); } else { // 如果一個類的實例, 最後須要調用一下 callParent() 方法 /** * var panel = new Ext.Panel({ ... }); * Ext.override(panel, { * initComponent: function () { * // extra processing... * * this.callParent(); * } * }); */ var owner = target.self, name, value; if (owner && owner.$isClass) { for (name in overrides) { if (overrides.hasOwnProperty(name)) { value = overrides[name]; if (typeof value == 'function') { //<debug> if (owner.$className) { value.displayName = owner.$className + '#' + name; } //</debug> value.$name = name; value.$owner = owner; value.$previous = target.hasOwnProperty(name) ? target[name] // already hooked, so call previous hook : callOverrideParent; // calls by name on prototype } target[name] = value; } } } else { // 若target只是一個普通對象,則調用apply方法便可。 Ext.apply(target, overrides); } } return target; } });
3.3 初始化一些類型驗證函數,這裏只記錄了兩個不常見的,剩下的也比較簡單spa
Ext.apply(Ext, { ...... // 判斷是不是 HTMLElement isElement: function(value) { return value ? value.nodeType === 1 : false; }, // 判斷是不是TextNode isTextNode: function(value) { return value ? value.nodeName === "#text" : false; }, ...... });
3.4
Ext.apply(Ext, { // 複製對象,包括 [], {}, dom, date. 不會產生引用對象。 clone: function(item) { var type, i, j, k, clone, key; if (item === null || item === undefined) { return item; } // DOM nodes if (item.nodeType && item.cloneNode) { return item.cloneNode(true); } type = toString.call(item); // Date if (type === '[object Date]') { return new Date(item.getTime()); } // Array if (type === '[object Array]') { i = item.length; clone = []; while (i--) { clone[i] = Ext.clone(item[i]); } } // Object else if (type === '[object Object]' && item.constructor === Object) { clone = {}; for (key in item) { clone[key] = Ext.clone(item[key]); } if (enumerables) { for (j = enumerables.length; j--;) { k = enumerables[j]; if (item.hasOwnProperty(k)) { clone[k] = item[k]; } } } } return clone || item; }, ...... });
4. Ext.globalEval
Ext.globalEval = Ext.global.execScript ? function(code) { // exexScript做用域是全局閉包 execScript(code); } : function($$code) { (function(){ // 這裏須要讓 Ext 指代的是全局變量的Ext對象 var Ext = this.Ext; eval($$code); }()); };
這裏還須要多說兩句的是 eval & window.eval & window.execScript 的區別
首先 eval 和 window.eval 的區別,能夠參考這篇文章。簡單點說,就是eval是局部閉包,而window.eval是全局閉包
window.execScript只有IE認識,且也是全局閉包。之後有時間再來詳細分析一下三者之間的區別。