`

Ext.js源代码分析

 
阅读更多
文件:ext-2.3.0/source/core/Ext.js
 
概述:Ext.js主要负责创建Ext全局对象,构建其命名空间,定义extend类继承方法,探测浏览器信息和对Javascript原生库进行扩展。
 
分析:
 
一、创建Ext全局对象
Js代码  
// 创建Ext全局对象,大多数JS库为了避免和其他JS库命名冲突,都会把自己创建的类或函数封装到一个全局变量中去,  
// 这样就相当于创造了自己的命名空间,可以算是一个单例模式。例如,jQuery就是全部都封装到$变量中去。  
Ext = {version: '2.3.0'};  
 
二、设置全局undefined变量
Js代码  
// 兼容旧浏览器,早期的浏览器实现中,undefined并不是全局变量。就是说,你要判断一个变量是否是没定义,  
// 你需要这样写if (typeof  a == 'undefined'),不可以写成if (a == undefined)。所以,上面的代码就可以理解了。  
// 右面的window["undefined"],因为window对象没有undefined属性,所以其值为undefined,  
// 把undefined赋值给window的undefined属性上,就相当于把undefined设置成了全局变量,  
// 这样以后你再判断一个变量是否是未定义的时候,就不需要使用typeof,直接判断就可以了。  
window["undefined"] = window["undefined"];  
 
三、定义apply方法属性复制函数
Js代码  
// apply方法,把对象c中的属性复制到对象o中,支持默认属性defaults设置。这个方法属于对象属性的一个浅拷贝函数。  
Ext.apply = function(o, c, defaults){  
    if(defaults){  
        // 如果默认值defaults存在,那么先把defaults上得属性复制给对象o  
        Ext.apply(o, defaults);  
    }  
    if(o && c && typeof c == 'object'){  
        for(var p in c){  
            o[p] = c[p];  
        }  
    }  
    return o;  
};  
 
四、扩展Ext对象
Js代码  
(function(){  
    // idSeed,用来生成自增长的id值。  
    var idSeed = 0;  
  
    // ua,浏览器的用户代理,主要用来识别浏览器的型号、版本、内核和操作系统等。  
    var ua = navigator.userAgent.toLowerCase(),  
        check = function(r){  
            return r.test(ua);  
        },  
        
        // isStrict,表示当前浏览器是否是标准模式。  
        // 如果正确的设置了网页的doctype,则compatMode为CSS1Compat,否则为BackCompat  
        isStrict = document.compatMode == "CSS1Compat",  
  
        // isOpera,表示是否是opera浏览器。  
        isOpera = check(/opera/),  
  
        // isChrome,表示是否是谷歌浏览器。  
        isChrome = check(/chrome/),  
  
        // isWebKit,表示当前浏览器是否使用WebKit引擎。  
        // WebKit是浏览器内核,Safari和Chrome使用WebKit引擎。  
        isWebKit = check(/webkit/),  
  
        // isSafari,表示是否是苹果浏览器,下面代码是对其版本识别。  
        isSafari = !isChrome && check(/safari/),  
        isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2  
        isSafari3 = isSafari && check(/version\/3/),  
        isSafari4 = isSafari && check(/version\/4/),  
  
        // isIE,表示是否是IE浏览器,下面代码是对其版本识别。  
        isIE = !isOpera && check(/msie/),  
        isIE7 = isIE && check(/msie 7/),  
        isIE8 = isIE && check(/msie 8/),  
        isIE6 = isIE && !isIE7 && !isIE8,  
  
        // isGecko,表示当前浏览器是否使用Gecko引擎。  
        // Gecko是浏览器内核,Firefox使用Gecko引擎。  
        isGecko = !isWebKit && check(/gecko/),  
        isGecko2 = isGecko && check(/rv:1\.8/),  
        isGecko3 = isGecko && check(/rv:1\.9/),  
  
        // isBorderBox,表示浏览器是否是IE的盒模式。  
        // 众所周知,IE的盒模式和W3C的盒模式不一致。当IE浏览器在怪异模式下,就会导致错误的盒模式。  
        isBorderBox = isIE && !isStrict,  
  
        // isWindows,表示是否是windows操作系统。  
        isWindows = check(/windows|win32/),  
  
        // isMac,表示是否是苹果操作系统。  
        isMac = check(/macintosh|mac os x/),  
  
        // isAir,AIR(Adobe Integrated Runtime),是adobe开发的一个平台吧,不太了解,没用过。  
        isAir = check(/adobeair/),  
  
        // isLinux,表示是否是Liunx操作系统。  
        isLinux = check(/linux/),  
  
        // isSecure,表示是否是https连接。  
        isSecure = /^https/i.test(window.location.protocol);  
  
    // 缓存一下CSS的背景图像,防止图像闪烁,应该是IE6的一个bug。  
    if(isIE6){  
        try{  
            document.execCommand("BackgroundImageCache", false, true);  
        }catch(e){}  
    }  
  
    // 扩展Ext对象,有一些属性,这个文件中没有使用,现在先不解释其作用,后面遇到了再讲。  
    Ext.apply(Ext, {  
  
        // isStrict,表示是否是标准模式。  
        isStrict : isStrict,  
  
        // isSecure,表示是否是https连接。  
        isSecure : isSecure,  
  
        // isReady,表示Dom文档树是否加载完成  
        isReady : false,  
  
        // enableGarbageCollector和enableListenerCollection这两个变量在Element中使用了,解析到Element时再解释其含义。  
        enableGarbageCollector : true,  
  
        enableListenerCollection:false,  
  
        // SSL_SECURE_URL,这个值在构造隐藏的iframe时,用来设置src属性的,只是当是https连接的时候才用。  
        SSL_SECURE_URL : "javascript:false",  
  
        // BLANK_IMAGE_URL,1像素透明图片地址  
        BLANK_IMAGE_URL : "http:/"+"/extjs.com/s.gif",  
  
        // emptyFn,空函数  
        emptyFn : function(){},  
  
        // applyIf,把对象c的属性复制到对象o上,只复制o没有的属性  
        applyIf : function(o, c){  
            if(o && c){  
                for(var p in c){  
                    if(typeof o[p] == "undefined"){ o[p] = c[p]; }  
                }  
            }  
            return o;  
        },  
  
        // addBehaviors函数可以一次给多个Ext.Element添加不同的事件响应函数  
        addBehaviors : function(o){  
  
            // 判断Dom树是否已经加装成功  
            if(!Ext.isReady){  
                // 如果Dom树没有加载好,那么等到加载好了,再执行此函数  
                Ext.onReady(function(){  
                    Ext.addBehaviors(o);  
                });  
                return;  
            }  
  
            // cache,简单缓存一下选择过的CSS Selector。  
            var cache = {};   
  
            // 遍历对象o,b的格式应该是selector@eventName  
            for(var b in o){  
  
                // parts[0]=selector,parts[1]=eventName  
                var parts = b.split('@');  
                if(parts[1]){  
                    // 如果事件名称存在,s为selector  
                    var s = parts[0];  
  
                    // 判断一下cache缓存中是否已经查询过该selector  
                    if(!cache[s]){  
  
                        // 如果没有查询过,那么用select方法查询一下  
                        cache[s] = Ext.select(s);  
                    }  
  
                    // 调用Element的on方法来注册事件函数  
                    cache[s].on(parts[1], o[b]);  
                }  
            }  
  
            // 释放cache变量,防止内存泄露  
            cache = null;  
        },  
  
        // 取得el的id属性。el可以是Ext.Element或者是Dom元素。  
        // 如果el不存在,那么生成一个自增长的id,并返回这个id。  
        // 如果el存在,但是没有id属性,那么生成一个自增长的id,并赋值给el的id属性,最后返回id值。  
        // 如果el存在,并且也有id属性,那么直接返回el的id值。  
        // prefix表示生成自增长的id的前缀,默认值为ext-gen  
        id : function(el, prefix){  
            prefix = prefix || "ext-gen";  
            el = Ext.getDom(el);  
            var id = prefix + (++idSeed);  
            return el ? (el.id ? el.id : (el.id = id)) : id;  
        },  
  
        // 类继承函数,基于javascript的prototype,模仿面相对象的继承特性。  
        // 整个ExtJS框架的继承机制就是这个函数实现的。  
        extend : function(){  
            // override函数,用来覆盖prototype上的属性的(私有对象,仅下面的return function内部可以使用)  
            var io = function(o){  
                for(var m in o){  
                    this[m] = o[m];  
                }  
            };  
  
            // Object的构造函数(私有对象,仅下面的return function内部可以使用)  
            var oc = Object.prototype.constructor;  
  
            return function(sb, sp, overrides){  
                // sb表示subclass,sp表示superclass,overrides是默认值为对象型  
                // 如果sp是对象,表示没有传sb变量进来,所以重新设置一下参数  
                if(typeof sp == 'object'){  
                    overrides = sp;  
                    sp = sb;  
                    // 如果overrides中提供了构造函数,那么就用提供的,  
                    // 否则用下面这个匿名函数,匿名函数会调用父类的构造函数  
                    sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};  
                }  
  
                // F是一个临时的类,其prototype指向superclass的prototype,  
                // 同时也把subclass的prototype指向了F对象,  
                // 这样可以避免在类继承的时候,调用superclass的构造函数  
                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.override = io;  
  
                // 设置默认值  
                Ext.override(sb, overrides);  
  
                // 继承函数,这样写方便,可以直接从别的类继承新类  
                sb.extend = function(o){Ext.extend(sb, o);};  
                return sb;  
            };  
        }(),  
  
        // 覆盖函数,直接把属性复制到origclass的prototype上  
        override : function(origclass, overrides){  
            if(overrides){  
                var p = origclass.prototype;  
                for(var method in overrides){  
                    p[method] = overrides[method];  
                }  
  
                // 下面是处理IE浏览器在枚举对象的属性时,  
                // 原生的方法toString枚举不出来,即使是自定义的toString也不行  
                if(Ext.isIE && overrides.toString != origclass.toString){  
                    p.toString = overrides.toString;  
                }  
            }  
        },  
  
        // 生成命名空间。javascript语言没有命名空间这么一说,所以只好用对象的属性来实现。  
        namespace : function(){  
            var a=arguments, o=null, i, j, d, rt;  
            for (i=0; i<a.length; ++i) {  
                d=a[i].split(".");  
                rt = d[0];  
                eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');  
                for (j=1; j<d.length; ++j) {  
                    o[d[j]]=o[d[j]] || {};  
                    o=o[d[j]];  
                }  
            }  
        },  
  
        // URL编码函数  
        urlEncode : function(o){  
            if(!o){  
                return "";  
            }  
            var buf = [];  
            for(var key in o){  
                var ov = o[key], k = encodeURIComponent(key);  
                var type = typeof ov;  
                if(type == 'undefined'){  
                    buf.push(k, "=&");  
                }else if(type != "function" && type != "object"){  
                    buf.push(k, "=", encodeURIComponent(ov), "&");  
                }else if(Ext.isDate(ov)){  
                    var s = Ext.encode(ov).replace(/"/g, '');  
                    buf.push(k, "=", s, "&");  
                }else if(Ext.isArray(ov)){  
                    if (ov.length) {  
                        for(var i = 0, len = ov.length; i < len; i++) {  
                            buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");  
                        }  
                    } else {  
                        buf.push(k, "=&");  
                    }  
                }  
            }  
            buf.pop();  
            return buf.join("");  
        },  
  
        // URL解码函数  
        urlDecode : function(string, overwrite){  
            if(!string || !string.length){  
                return {};  
            }  
            var obj = {};  
            var pairs = string.split('&');  
            var pair, name, value;  
            for(var i = 0, len = pairs.length; i < len; i++){  
                pair = pairs[i].split('=');  
                name = decodeURIComponent(pair[0]);  
                value = decodeURIComponent(pair[1]);  
                if(overwrite !== true){  
                    if(typeof obj[name] == "undefined"){  
                        obj[name] = value;  
                    }else if(typeof obj[name] == "string"){  
                        obj[name] = [obj[name]];  
                        obj[name].push(value);  
                    }else{  
                        obj[name].push(value);  
                    }  
                }else{  
                    obj[name] = value;  
                }  
            }  
            return obj;  
        },  
  
        // each函数,迭代数组时候用的,和jQuery的each方法差不多,不过Ext的each只能迭代数组或者类数组  
        each : function(array, fn, scope){  
            if(typeof array.length == "undefined" || typeof array == "string"){  
                array = [array];  
            }  
            for(var i = 0, len = array.length; i < len; i++){  
                if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };  
            }  
        },  
  
        // 组合函数,标识了过期不推荐,这里也就不看了  
        combine : function(){  
            var as = arguments, l = as.length, r = [];  
            for(var i = 0; i < l; i++){  
                var a = as[i];  
                if(Ext.isArray(a)){  
                    r = r.concat(a);  
                }else if(a.length !== undefined && !a.substr){  
                    r = r.concat(Array.prototype.slice.call(a, 0));  
                }else{  
                    r.push(a);  
                }  
            }  
            return r;  
        },  
  
        // 处理需要转义的字符,在需要转义的字符前面多加一个反斜线  
        escapeRe : function(s) {  
            return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");  
        },  
  
        // 回调函数,可以指定this值和参数,还可以延迟执行  
        callback : function(cb, scope, args, delay){  
            if(typeof cb == "function"){  
                if(delay){  
                    cb.defer(delay, scope, args || []);  
                }else{  
                    cb.apply(scope, args || []);  
                }  
            }  
        },  
  
        // 取得html元素,el可以是id,也可以是Ext的Element对象  
        getDom : function(el){  
            if(!el || !document){  
                return null;  
            }  
            return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);  
        },  
  
        // 取得document的Element对象  
        getDoc : function(){  
            return Ext.get(document);  
        },  
  
        // 取得文档对象。  
        // document.body = document.getElementsByTagName('body')[0];  
        // document.documentElement = document.getElementsByTagName('html')[0];  
        getBody : function(){  
            return Ext.get(document.body || document.documentElement);  
        },  
  
        // 取得组件对象  
        getCmp : function(id){  
            return Ext.ComponentMgr.get(id);  
        },  
  
        // 这个用来取得数字,可以设置默认值  
        num : function(v, defaultValue){  
            v = Number(v == null || typeof v == 'boolean'? NaN : v);  
            return isNaN(v)? defaultValue : v;  
        },  
  
        // 销毁函数,销毁的对象可以是Element或者是Component  
        destroy : function(){  
            for(var i = 0, a = arguments, len = a.length; i < len; i++) {  
                var as = a[i];  
                if(as){  
                    if(typeof as.destroy == 'function'){  
                        as.destroy();  
                    }  
                    else if(as.dom){  
                        as.removeAllListeners();  
                        as.remove();  
                    }  
                }  
            }  
        },  
  
        // 清除Dom节点n  
        removeNode : isIE ? function(){  
            var d;  
            return function(n){  
                if(n && n.tagName != 'BODY'){  
                    d = d || document.createElement('div');  
                    d.appendChild(n);  
                    d.innerHTML = '';  
                }  
            }  
        }() : function(n){  
            if(n && n.parentNode && n.tagName != 'BODY'){  
                n.parentNode.removeChild(n);  
            }  
        },  
  
        // javascript是一个弱类型的语音,所以下面这个type函数可以正确返回测试变量的类型  
        type : function(o){  
            // 其实undefined和null也可以算两种类型,这里把他们俩全归类到false了  
            if(o === undefined || o === null){  
                return false;  
            }  
            if(o.htmlElement){  
                return 'element';  
            }  
            var t = typeof o;  
            if(t == 'object' && o.nodeName) {  
                switch(o.nodeType) {  
                    case 1: return 'element';  
                    case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';  
                }  
            }  
            if(t == 'object' || t == 'function') {  
                switch(o.constructor) {  
                    case Array: return 'array';  
                    case RegExp: return 'regexp';  
                    case Date: return 'date';  
                }  
                if(typeof o.length == 'number' && typeof o.item == 'function') {  
                    return 'nodelist';  
                }  
            }  
            return t;  
        },  
  
        // 判断v是否为空或者未定义,allowBlank表示是否允许空字符串,默认值是false。  
        // 当allowBlank设置为true时,isEmpty('')返回false  
        isEmpty : function(v, allowBlank){  
            return v === null || v === undefined || (!allowBlank ? v === '' : false);  
        },  
  
        // 判断v是否为空,为空的话可以设置默认值,不为空的话返回v值  
        value : function(v, defaultValue, allowBlank){  
            return Ext.isEmpty(v, allowBlank) ? defaultValue : v;  
        },  
  
        // 判断v是否是数组对象  
        isArray : function(v){  
            return v && typeof v.length == 'number' && typeof v.splice == 'function';  
        },  
  
        // 判断v是否是日期对象  
        isDate : function(v){  
            return v && typeof v.getFullYear == 'function';  
        },  
  
        // isOpera,表示是否是opera浏览器。  
        isOpera : isOpera,  
  
        // isWebKit,表示当前浏览器是否使用WebKit引擎。  
        isWebKit: isWebKit,  
  
        // isChrome,表示是否是谷歌浏览器。  
        isChrome : isChrome,  
  
        // isSafari,表示是否是苹果浏览器,下面代码是对其版本识别。  
        isSafari : isSafari,  
   
        isSafari4 : isSafari4,  
  
        isSafari3 : isSafari3,  
  
        isSafari2 : isSafari2,  
  
        // isIE,表示是否是IE浏览器,下面代码是对其版本识别。  
        isIE : isIE,  
  
        isIE6 : isIE6,  
  
        isIE7 : isIE7,  
  
        isIE8 : isIE8,  
  
        // isGecko,表示当前浏览器是否使用Gecko引擎。  
        isGecko : isGecko,  
  
        isGecko2 : isGecko2,  
  
        isGecko3 : isGecko3,  
  
        // isBorderBox,表示浏览器是否是IE的盒模式。  
        isBorderBox : isBorderBox,  
  
        // isLinux,表示是否是Liunx操作系统。  
        isLinux : isLinux,  
  
        // isWindows,表示是否是windows操作系统。  
        isWindows : isWindows,  
  
        // isMac,表示是否是苹果操作系统。  
        isMac : isMac,  
  
        // isAir,AIR(Adobe Integrated Runtime)  
        isAir : isAir,  
  
        // useShims,表示是IE 6浏览器或者是苹果系统上的Firefox浏览器,并且gecko内核版本小于3。  
        // 具体哪里使用到了,还不知道,读到后面代码发现了,再解释。  
        useShims : ((isIE && !(isIE7 || isIE8)) || (isMac && isGecko && !isGecko3))  
    });  
  
    // namespace函数的简写方式  
    Ext.ns = Ext.namespace;  
})();   
 
五、创建Ext所用的命名空间
Js代码  
Ext.ns("Ext", "Ext.util", "Ext.grid", "Ext.dd", "Ext.tree", "Ext.data",  
                "Ext.form", "Ext.menu", "Ext.state", "Ext.lib", "Ext.layout", "Ext.app", "Ext.ux");  
 
六、扩展原生Function
Js代码  
Ext.apply(Function.prototype, {  
  
    // 创建回调函数,这个有点太简单了,并且this指向了window,不可以自定义。功能不是很强  
    createCallback : function(/*args...*/){  
        var args = arguments;  
        var method = this;  
        return function() {  
            return method.apply(window, args);  
        };  
    },  
  
    // 创建委托(注:Delegate在C#里是叫委托的,其实就是c语音里的函数指针,js中叫匿名函数)  
    // createDelegate比createCallback高级了一点可以设置this指针,同时也可以设置传入的参数  
    createDelegate : function(obj, args, appendArgs){  
        var method = this;  
        return function() {  
            var callArgs = args || arguments;  
            if(appendArgs === true){  
                callArgs = Array.prototype.slice.call(arguments, 0);  
                callArgs = callArgs.concat(args);  
            }else if(typeof appendArgs == "number"){  
                callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first  
                var applyArgs = [appendArgs, 0].concat(args); // create method call params  
                Array.prototype.splice.apply(callArgs, applyArgs); // splice them in  
            }  
            return method.apply(obj || window, callArgs);  
        };  
    },  
  
    // defer是createDelegate的延迟版,可以延迟执行  
    defer : function(millis, obj, args, appendArgs){  
        var fn = this.createDelegate(obj, args, appendArgs);  
        if(millis){  
            return setTimeout(fn, millis);  
        }  
        fn();  
        return 0;  
    },  
  
    // 这个函数可以在你执行完原函数以后,执行一下自定义的函数。  
    createSequence : function(fcn, scope){  
        if(typeof fcn != "function"){  
            return this;  
        }  
        var method = this;  
        return function() {  
            var retval = method.apply(this || window, arguments);  
            fcn.apply(scope || this || window, arguments);  
            return retval;  
        };  
    },  
  
    // 这个就是完全的函数代理了,和Spring的AOP是一个概念。  
    createInterceptor : function(fcn, scope){  
        if(typeof fcn != "function"){  
            return this;  
        }  
        var method = this;  
        return function() {  
            fcn.target = this;  
            fcn.method = method;  
            if(fcn.apply(scope || this || window, arguments) === false){  
                return;  
            }  
            return method.apply(this || window, arguments);  
        };  
    }  
});  
 
七、扩展原生String
Js代码  
Ext.applyIf(String, {  
  
    // 转义单引号和反斜杠  
    escape : function(string) {  
        return string.replace(/('|\\)/g, "\\$1");  
    },  
  
    // 这个函数是对数组进行空格补位  
    leftPad : function (val, size, ch) {  
        var result = new String(val);  
        if(!ch) {  
            ch = " ";  
        }  
        while (result.length < size) {  
            result = ch + result;  
        }  
        return result.toString();  
    },  
  
    // 这个是格式化字符串,很多语言都有的功能  
    format : function(format){  
        var args = Array.prototype.slice.call(arguments, 1);  
        return format.replace(/\{(\d+)\}/g, function(m, i){  
            return args[i];  
        });  
    }  
});  
  
// 切换值函数  
String.prototype.toggle = function(value, other){  
    return this == value ? other : value;  
};  
  
// 去空格函数  
String.prototype.trim = function(){  
    var re = /^\s+|\s+$/g;  
    return function(){ return this.replace(re, ""); };  
}();  
 
八、扩展原生Number
Js代码  
Ext.applyIf(Number.prototype, {  
    // 对当前数值取一个范围  
    constrain : function(min, max){  
        return Math.min(Math.max(this, min), max);  
    }  
});  
 
九、扩展原生Array
Js代码  
Ext.applyIf(Array.prototype, {  
      
    indexOf : function(o){  
       for (var i = 0, len = this.length; i < len; i++){  
          if(this[i] == o) return i;  
       }  
       return -1;  
    },  
  
      
    remove : function(o){  
       var index = this.indexOf(o);  
       if(index != -1){  
           this.splice(index, 1);  
       }  
       return this;  
    }  
});  
 
十、扩展原生Date
Js代码  
// 返回一个时间差  
Date.prototype.getElapsed = function(date) {  
    return Math.abs((date || new Date()).getTime()-this.getTime());  
};  

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics