ExtJs 源码笔记------Ext.js
最近准备系统的学习一下Ext的源码,SO,话不多说,从第一篇开始。
首先,先看一下Ext.js这个文件的代码结构:
var Ext = Ext || {}; // 定义全局变量 Ext._startTime = new Date().getTime(); (function(){ // 定义一些局部变量 var ...... // 核心 Ext.apply = function(){......}; // 初始化 Ext 的一些属性函数 Ext.apply(Ext, {......}); }()); // 全局闭包 Ext.globalEval = ......
代码的结构不难,但是仔细看下来,有些细节的地方还是很值得回味一番。下面具体分析一下我对源码的理解,水平有限,不足之处还望各位看官指正。
1. 定义局部变量
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.apply
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中拷贝对象的内容,可以狠狠的点击这里。
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.2
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 初始化一些类型验证函数,这里只记录了两个不常见的,剩下的也比较简单
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认识,且也是全局闭包。以后有时间再来详细分析一下三者之间的区别。