在JavaScript中,this通常绑定到函数被调用的对象上,这种默认绑定在多数情况下是正常的,但是在某些情况下,this的这种绑定会丢失,比如,将函数作为参数传递给另外一个函数,这种默认的绑定就丢失了,例如:
var myObj = {
name: 'A nice demo!',
fx: function() {
alert(this.name);
}
};
function runFx(fx) {
fx();
}
window.name = 'I am a nice window';
myObj.fx(); // 在这里,将会提示 A nice demo!
runFx(myObj.fx); // 在这里,得到的提示将会是 I am a nice window
为什么会出现这种情况呢?因为在第二次调用中,myObj.fx作为参数传递给了runFx,在函数runFx中执行时fx丢失了对myObj的默认绑定,而绑定到了runFx的默认绑定window上,所以第二次调用时得到的提示为I am a nice window,而这个往往不是我们需要的结果。
在Ajax横行的今天,要编写复杂的客户端组件,不可避免的要将函数名作为参数传递,Prototype注意到了这个问题,MS ASP.Net Ajax也注意到了这个问题,不知道你注意了没有?
Prototype提供的解决方案是bind方法,在Prototype的官方文档给出的描述是:Provides aguaranteed-binding equivalent of the original function, possibly with pre-filled arguments. 上面的例子如果使用Prototype的话,可以修改如下:
var myObj = {
name: 'A nice demo!',
fx: function() {
alert(this.name);
}
};
function runFx(fx) {
fx();
}
window.name = 'I am a nice window';
myObj.fx(); // 在这里,将会提示 A nice demo!
var fx2 = myObj.fx.bind(myObj) // 先做一个绑定,
runFx(fx2); // 在这里,得到的提示将会是 A nide demo !这个往往是我们需要的结果
MS Ajax提供的解决方案是Function.createDelegate函数,createDelegate方法是个静态方法,可以直接调用。如果使用MS Ajax库的话,可以将上面的例子修改为:
var myObj = {
name: 'A nice demo!',
fx: function() {
alert(this.name);
}
};
function runFx(fx) {
fx();
}
window.name = 'I am a nice window';
myObj.fx(); // 在这里,将会提示 A nice demo!
var fx2 = Function.createDelegate(myObj, myObj.fx) // 按照微软的说法,先做一个委托,
runFx(fx2); // 在这里,得到的提示将会是 A nide demo !这个往往是我们需要的结果
这看起来只是一个小问题,但是如果不注意的话却会造成很大的问题。引用Prototype中的原话:As discussed on the general Function page,binding can be a pretty tricky thing sometimes.