日期:2014-05-16 浏览次数:20393 次
?ExtJs对JavaScript的内建对象进行了扩展,对什么Object、Date、Array、Function、String的扩展,扩展方法想必诸位都烂熟于心了:用prototype的办法。这一篇讲一讲Function扩展的精妙之处,之所以突然研究这个问题,是因为我在研究 Ext.data.Store的源代码时,看到一行代码:
this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
当初,我在研究Ext.js中的代码时,对于Function的几个扩展想不透、看不明,今日大悟。且见扩展的源代码:
??? 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);
??????? };
??? },
createDelegate函数的作用是,为指定函数创建一个回调函数,注意是创建一个新的函数返回,它返回的是一个新函数。我以前一直不明白,为什么要这么做,就像上面红色的那行代码,相信大伙与我一样,都在想,为什么不是写成这样:
this.reader.onMetaChange=this.onMetaChange;
不是应当这样写的吗?如果用过dotnet,那么委托一定是晓得了,javascript中的函数跟c#的委托一样,有很相近的意义,为什么c#中能这样写,JavaScript中不能这样写呢?
一切都因为this,this这个东西见风使舵,像上面onMetaChange这函数,实际调用时是在reader中,那么如果 onMetaChange中使用了this关键字,那么,this是指向reader的,而不是指向onMetaChange的定义环境所对应的 this。而事实上,我们往往想把这个this指向函数的定义环境,这也正是回调的最招人喜欢的地方,然而,因为this的问题,回调就不能像上面那样直接赋值。还得做些手脚,得让函数调用时scope为当前定义环境。
改变一个函数执行的scope,熟翻JavaScript的兄弟一定晓得要用:call、apply。至此,createDelegate的产生背景、作用都作了个交代。
createDelegate(this),调用时,一般直接传个this就行了,当真是妙啊。事实上,我上面讲的一通道理清楚了,这个函数的代码就没有秘密可言了。关键就是一个this。我现在感叹,你对JavaScript的造诣与你对this的领悟层次成正比。
既然讲了createDelegate,其他几个扩展函数一并讲了。
??? createCallback : function(/*args...*/){
??????? // make args available, in function below
??????? var args = arguments;
??????? var method = this;
??????? return function() {
??????????? return method.apply(window, args);
??????? };
??? }
也是创建调用者的回调,不过,回调函数的scope为window。相当于createDelegate(window)。没什么讲的。
??? defer : function(millis, obj, args, appendArgs){
??????? var fn = this.createDelegate(obj, args, appendArgs);
??????? if(millis){
??????????? return setTimeout(fn, millis);
??????? }
??????? fn();
??????? return 0;
??? },
此函数调用一次就让函数延迟调用一次。对setTimeout的封装罢了。如果没有定义延时参数,那么就马上执行。这个函数也没有技术性可言。
??? 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;
??????? };
??? },
这个函数就有点意思了,刚开始研究ext.js的时候还没有看明白,它的作用是在返回一个函数,此函数先调用“调用函数”,后调用传递进来的函数。这句话可能还没说清,见示例如下:
function A(){alert("第一个执行!");return 1;}
function B(){alert("第二个执行!");return 2;}
function C(){alert("第三个执行!");return 3;}
var D=A.createSequence(B).createSequence(C);
var result=D();
上面代码产生的效果是:
第一弹出框显示:第一个执行!
第二弹出框显示:第二个执行!
第三弹出框显示:第三个执行!
result的值为:3。
这下子诸位都明白了吧。用过dotnet的知道,委托变量有这种类似的功能。就是累加执行的效果。
??? 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);
??????? };
???