函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其实就是 Function.prototype.bind(),只是你有可能仍然没有意识到这点。
第一次遇到这个问题的时候,你可能倾向于将this设置到一个变量上,这样你可以在改变了上下文之后继续引用到它。很多人选择使用 self, _this 或者 context 作为变量名称(也有人使用 that)。这些方式都是有用的,当然也没有什么问题。但是其实有更好、更专用的方式。
Jack Archibald 关于缓存 this 的微博(twitter):
Jake Archibald: “我会为了作用域做任何事情,但是我不会使用 that = this”
我对这个问题更清晰的认识是在我看到Sindre Sorhus更清楚的描述之后:
Sindre Sorhus:“在jQuery中使用$this,但是对于纯JS我不会,我会使用.bind()”
而我在一开始的几个月里却忽略了这个明智的建议。
我们真正需要解决的问题是什么?
在下面的例子代码中,我们可以名正言顺地将上下文缓存到一个变量中:
var myObj = {
specialFunction: function () {
},
anotherSpecialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
var that = this;
this.getAsyncData(function () {
that.specialFunction();
that.anotherSpecialFunction();
});
}
};
myObj.render();
如果我们简单地使用 this.specialFunction() 来调用方法的话,会收到下面的错误
Uncaught TypeError: Object [object global] has no method 'specialFunction'
我们需要为回调函数的执行保持对 myObj 对象上下文的引用。 调用 that.specialFunction()让我们能够维持作用域上下文并且正确执行我们的函数。 然而使用 Function.prototype.bind() 可以有更加简洁干净的方式:
render: function () {
this.getAsyncData(function () {
this.specialFunction();
this.anotherSpecialFunction();
}.bind(this));
}
我们刚才做了什么?
.bind()创建了一个函数,当这个函数在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,我们传入想要的上下文,this(其实就是 myObj),到.bind()函数中。然后,当回调函数被执行的时候, this 便指向 myObj 对象。
如果有兴趣想知道 Function.prototype.bind() 内部长什么样以及是如何工作的,这里有个非常简单的例子:
Function.prototype.bind = function (scope) {
var fn = this;
return function () {
return fn.apply(scope);
};
}
还有一个非常简单的用例:
var foo = {
x: 3
}
var bar = function(){
console.log(this.x);
}
bar(); // undefined
var boundFunc = bar.bind(foo);
boundFunc(); // 3
我们创建了一个新的函数,当它被执行的时候,它的 this 会被设置成 foo —— 而不是像我们调用 bar() 时的全局作用域。