讀Ext之三(原型擴展)


續上篇,

Ext.ns = Ext.namespace;  

有了一個簡寫的namespace。整個匿名函數執行完了。接下來

Ext.ns("Ext.util", "Ext.lib", "Ext.data");  
Ext.elCache = {};  

分別為Ext添加了util,lib,data,elCache屬性,默認都是空的對象。

Ext.apply(Function.prototype, {
	createInterceptor : function(){
		// ...
	},
	createCallback : function(){
		// ...
	},
	createDelegate : function(){
		// ...
	},
	defer : function(){
		// ...
	}
});

 

Ext.apply在第一篇已經講到,這里用來擴展Function,為其增加了四個方法createInterceptor、createCallback、createDelegate、defer。
眾多流行的JS庫都不同程度的“污染”了原生JS,最典型的如Prototype ,Mootools 。JQuery則完全例外,一個匿名函數執行后便誕生了集所有API為一身的強大 $ .
雖然如此,JQuery的事件對象則不是DOM中事件對象,它完全重寫了一個兼容IE,DOM3的事件對象。這里的污染加了雙引號,不贊同的就理解成擴展吧。

 

createInterceptor 方法

createInterceptor : function(fcn, scope){
    var method = this;
    return !Ext.isFunction(fcn) ?
            this :
            function() {
                var me = this,
                    args = arguments;
                fcn.target = me;
                fcn.method = method;
                return (fcn.apply(scope || me || window, args) !== false) ?
                        method.apply(me || window, args) :
                        null;
            };
},

Interceptor顧名思義,攔截器。它卻不是Strust2中的攔截器,但還是有部分相似之處。這里是利用所傳函數fcn攔截,如果fcn返回false,將被攔截,true才執行。

var method = this,因為是給Function.prototype擴展,因此其prototype上掛的所有的函數,函數內的this都是Function,即函數自身。示例可能更容易理解

Function.prototype.test = function(){
	alert(this);
};
function fn(){return 'test';}
fn.test();

給Function.prototype添加了一個test方法,定義函數fn,fn會自動繼承test方法,fn.test()時彈出的this可以看到就是fn自身。

!Ext.isFunction(fcn),這個條件表明如果所傳參數fcn不是一個function,那么將直接返回this,this即函數自身。或者說這時沒有進行任何攔截,原樣返回了該函數自身。

當fcn為一個function時,將執行 “:”后的分支,此時給fcn添加了兩個屬性target,method。target是me,me是this。


此時的this是什么呢?多數情況下是window,但整個function如果作為對象屬性存在時,this則是該對象。誰調用了createInterceptor,method就是誰。如:

function oldFn(){
	alert('test');
}
function ret(){
	return false;
}
var newFn = oldFn.createInterceptor(ret);
newFn();

oldFn繼承了createInterceptor方法,且調用了它,參數是ret。這時createInterceptor內部的method, fcn.method就是oldFn;me,fcn.target則為window對象。改成如下,me,fcn.target則為obj了。

 

function oldFn(){
	alert('test');
}
function ret(){
	return false;
}
var obj = {name:'jack'};
obj.method = oldFn.createInterceptor(ret);
obj.method();

 

再往下看
(fcn.apply(scope || me || window, args) !== false),所傳參數fcn被執行,執行上下文優先是scope,其次是me,最后是window。返回結果不等於false才執行method。method執行上下文先為me,me不存在則是window。

整個createInterceptor方法就看完了,下面是一個具體例子:

function sayName(name){
	alert('hello, ' + name);
}
function rule(name){
	return name == 'snandy';
}
var sayName2 = sayName.createInterceptor(rule);
sayName2('zhangsan'); // -> no alert
sayName2('snandy');   // -> "hello, snandy"

 

createCallback 方法

createCallback : function(/*args...*/){
    // make args available, in function below
    var args = arguments,
        method = this;
    return function() {
        return method.apply(window, args);
    };
},

這個方法非常有用,實現簡單。返回一個新函數,新函數內執行method(method是誰就不用再提了吧),會把外面的參數給傳進來。
初學者經常 糾結於給事件handler傳參數 。createCallback 解決了給DOM事件handler(監聽器)傳參問題。
createCallback 不僅用在DOM事件handler上你完全可以自定義事件,設計一套屬於自己的 觀察者模式 API。即每個類有自己的事件,如 LTMaps ,除了擁有屬性,方法還有許多事件,如移動地圖(move),縮放地圖(zoom)。Ext的眾多UI組件也都是這種模式,Ext.Panel 具有afterlayout,close等事件。

在給這些事件添加hanlder,又想傳參或許也會用到 createCallback。

再看createDelegate方法,創建一個代理函數

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 (Ext.isNumber(appendArgs)){
            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);
    };
},

createDelegate 比 createCallback 更強大,除了能解決回調函數傳參問題。還能控制:
1, 自定義參數是否覆蓋默認參數(如 DOM事件對象 作為handler第一個參數)
2, 自定義參數的位置

內部實現:取自身(var method=this),返回一個新function,該function調用自身(method.apply),同時指定上下文(obj||window)及參數(callArgs)。就這么簡單,細節之處在於對參數的控制。

1,只傳obj和args時將覆蓋回調函數默認參數(DOM事件對象)

<a href="#" id="aa">SINA</a>
<script type="text/javascript">
	var aa = document.getElementById('aa');
	function sayName(name){
		alert('hello, ' + name);
	}
	var sayName2 = sayName.createDelegate(aa,['jack']);
	aa.onclick = sayName2;
</script>

 

2,三個參數都傳,appendArgs為true時,默認參數(DOM事件對象)位置不變(第一個),自定義參數args在最后

<a href="#" id="aa">SINA</a>
<script type="text/javascript">
	var aa = document.getElementById('aa');
	function sayName(){		
		alert('實際參數長度:' + arguments.length);
		alert('hello, ' + arguments[0]);
		alert('hi, ' + arguments[1]);
	}
	var sayName2 = sayName.createDelegate(aa,['jack'],true);
	aa.onclick = sayName2;
</script>

 

3, 三個參數都傳,appendArgs為數字時將指定自定義參數的位置

<a href="#" id="aa">SINA</a>
<script type="text/javascript">
	var aa = document.getElementById('aa');
	function sayName(name){		
		alert('實際參數長度:' + arguments.length);
		alert('hello, ' + arguments[0]);
		alert('hi, '+ arguments[1]);
		alert('hi, '+ arguments[2]);
	}
	var sayName2 = sayName.createDelegate(aa,['jack','lily'],0);
	aa.onclick = sayName2;
</script>

 

此外,method的執行上下文應該增加this,這樣實用一些了。

return method.apply(obj || this || window, callArgs);

 

接着看defer,指定函數在多少毫秒后執行

defer : function(millis, obj, args, appendArgs){
    var fn = this.createDelegate(obj, args, appendArgs);
    if(millis > 0){
        return setTimeout(fn, millis);
    }
    fn();
    return 0;
}

內部實用了剛剛提到的createDelegate方法,指定函數的執行上下文及參數設定。如果millis是正數則延遲執行返回setTimeout的返回值(一個數字),有必要可用clearTimeout終止。否則立即執行,返回0。

 

好了,以上是對Function.prototype的擴展。接下來是對String,Array的擴展。使用Ext.applyIf,第一篇 已提到Ext.applyIf不會覆蓋已有的方法。

Ext.applyIf(String, {
    format : function(format){
        var args = Ext.toArray(arguments, 1);
        return format.replace(/\{(\d+)\}/g, function(m, i){
            return args[i];
        });
    }
});

 

給String添加靜態方法(類方法)format,可以把字符串中特殊寫法({0},{1})用指定的變量替換。如

var href = 'http://www.sina.com.cn', text = '新浪';
var s = String.format('<a href="{0}">{1}</a>', href, text);
alert(s); // --> <a href="http://www.sina.com.cn">新浪</a>

有點類似於JSP的EL表達式。

 

Array擴展,

Ext.applyIf(Array.prototype, {
    indexOf : function(o, from){
        var len = this.length;
        from = from || 0;
        from += (from < 0) ? len : 0;
        for (; from < len; ++from){
            if(this[from] === o){
                return from;
            }
        }
        return -1;
    },
    remove : function(o){
        var index = this.indexOf(o);
        if(index != -1){
            this.splice(index, 1);
        }
        return this;
    }
});

 

indexOf 再熟悉不過了吧,String早有了。用來查找元素是否在數組中,如果有則返回該元素在數組中的索引,否則返回-1。該方法在 ECMAScript 5 中已經引入,各瀏覽器的新版本都實現了。

reomve 刪除指定元素,如果存在該元素則刪除,返回數組自身。

 

好了,以上3篇是整個Ext-core的Ext.js。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM