createDelegate函数
createDelegate是Function的扩展,无论是网上还是书上对它的评价都很高。今天特地拿出来分析一番。
Ext源码分析与开发实例宝典 的例子
<div>1</div>
<div>2</div>
<div>3</div>
function addEvent(el,type,fn){
if(el.addEventlistener)el.addEventListener(type,fn,false);
if(el.attachEvent)el.attachEvent('on'+type,fn);
}
var doms = document.getElementsByTagName('div');
var length=doms.length;
for(var i=0;i<length,i++){
var msg = 'you click '+i;
var callback = function(){alert(msg);}
addEvent(doms[i],'click',callback);
}
在上边的例子中 期望点击不同的div 弹出相应的下标值,但是实际情况是 弹出的值全部都是3
因为 每次循环闭包中callback函数的函数体都会变化,而事件注册的都是这个函数的引用。所以循环结束i定格在3上。
这种错误很常见 解决办法就是 声明一个可以储存每次传入的值函数
var delegate=function(m){return function(){ alert(m)} }
由此可见仅仅在函数外层包裹一层函数就ok了。
一般来说代理函数的作用如下:
- 改变函数作用域
- 改变函数参数形式
- 改变函数内容
Ext3.0 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);
};
}
var applyArgs = [appendArgs, 0].concat(args);
//得到一个[n,0,...]的形式
Array.prototype.splice.apply(callArgs, applyArgs);
//splice(索引位置,删除的元素个数,添加的值。。。。);
首先这是一个典型的闭包 在调用createDelegate的后。 返回的函数中隐藏有被委托函数的引用 method,提前传入的参数数组args 和配置项appendArgs。作用域obj
在被委托函数调用后 根据实际传参arguments 以及之前的参数生成真正参数列表 在作用域obj下执行..
完整测试代码:
function isNum(v){
return !!v || Object.prototype.toString.call(v)==='[object Number]'
}
console.log(isNum(3))
Function.prototype.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 (isNum(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);
};
}
function test(){
for(var i =0 ; i<arguments.length;i++){
console.log(arguments[i]);
}
}
var M = new Object();
M.test=test.createDelegate(this,['a','b','c'],true);
M.test('d','e','f');
console.log('test2------------');
M.test2=test.createDelegate(this,['a','b','c'],1);
M.test2('d','e','f');