通常,使用此方法的主要原因是使当前this
可用于子函数或闭包。例如:
var myObject = {
param: 123,
method: function(){
alert( this.param );
},
method2: function(){
setTimeout(function(){
alert( this.param );
},100);
}
}
在上面的调用中,
myObject.method
将为您提供
123
的正确警报。但是,调用
myObject.method2
将给您
undefined
。这是因为与
this
一起使用的匿名函数中的
setTimeout
不引用
myObject
,具体取决于JavaScript解释器,它指向的是不同的事物。但是,如果您有:
method2: function(){
var self = this;
setTimeout(function(){
alert( self.param );
},100);
}
之所以可行,是因为在正确的位置捕获了
this
的当前状态,并将始终为每个可用的函数范围引用
myObject
。
问题不仅仅限于
setTimeout
的使用。在具有匿名函数,子函数或闭包的任何位置,此技巧都将派上用场。有时,根据当前引用表示的内容,人们使用
self
或
that
或更具描述性的内容。
而不是存储为变量
除了使用
self
或任何其他变量来“记住”在任何特定点的状态外,还有一种选择是将匿名或子函数与特定上下文“绑定(bind)”。现在,许多现代解释器支持
Function.prototype.bind
方法,因此可以使用:
var method = function(){
console.log(this);
};
var methodWithWindow = method.bind(window);
var methodWithDocument = method.bind(document);
var methodWithObject = method.bind({random:"object"});
依次调用每个绑定(bind)方法将为您提供以下控制台输出:
Window
Document
Object {random:"object"}
如果您希望支持较旧的浏览器,则可以使用
polyfill,或者如果您希望使用一种更简单的实现,则也不必担心绑定(bind)参数。绑定(bind)代码的基本操作如下:
!Function.prototype.bind && (Function.prototype.bind = function(context){
var method = this;
return function(){
method.apply(context, arguments);
}
})
那么,使用bind的初始示例的外观如何?
method2: function(){
setTimeout((function(){
console.log(this); // `this` will be the same as the `this` passed to bind.
}).bind(this),100);
}
如上所示,绑定(bind)后,返回的函数(闭包)将保留指定的上下文;因此可以将其传递到您想要的任何地方,并且仍然保留对所需对象的
this
引用。这在
method2
示例中很有用,因为我们将方法与当前上下文 bundle 在一起,然后将其传递给
setTimeout
,它将在以后(在退出当前块执行后很长时间)执行绑定(bind)的方法。
使用
self
或任何其他变量时,也会发生相同的情况。该变量将被捕获在函数的作用域链中,并且当最终再次调用该函数时,该变量仍将在那里访问。但是,使用
bind
的好处是,如果您愿意,可以轻松地覆盖上下文,您将必须编写自己的特定方法来覆盖
self
变量。
WARNING: It is worth noting here that when you bind a function, a new function is returned. This can cause confusing situations if you mix bound functions with event listeners and then attempt to remove the listeners using the original function rather than the bound version.
Also, because binding returns a new function, if you bind a bound function you are in fact wrapping a function in a function, with another function. You should be aware of this because it affects performance and will prove trickier to manage in terms of avoiding memory leaks. My preferred approach to binding is to use closures with their own deconstruction methods (i.e. rely on self, but make sure you have methods to nullify it's content), but this does take more forward thinking and is not so relevant in smaller JS projects; or one off function bindings — especially if the bound method is never caught in any reference.
没有自我约束?
还值得一提的是,有时您完全不需要使用
bind
即可获得相同的结果,而可以使用
apply
-可以在任何选择使用的方式中自然使用它。主要区别在于该函数没有包装任何东西,调用apply实际上在那里执行该函数,然后在不同的上下文中执行-传递给apply的第一个参数。
var externalMethod = function(){
console.log(this); // will output myObject when called below
};
var myObject = {
method2: function(){
externalMethod.apply(this);
}
};
什么是
this
?
只是为了详细说明有关
this
的答案,然后再删除最近的注释。
this
将引用四件事之一,具体取决于您在其中使用它的函数的调用方式:
myObject.method()
除非
this
应用了
myObject
操作,否则上面的
method
为
.bind(context)
。在这种情况下,
this
将是最后一个绑定(bind)上下文。
unattachedFunction()
将具有全局上下文的
this
(通常在浏览器环境中为
window
),除非
unattachedFunction
已应用
.bind(context)
操作。在这种情况下,
this
将是最后一个绑定(bind)上下文。
anyFunction.apply(otherObject)
或者
anyFunction.call(otherObject)
两者都将始终具有
this
的
otherObject
,因为以这种方式调用将覆盖任何绑定(bind)。
new myObject()
将具有一个
this
,它指向
myObject
的新实例,这将覆盖所有绑定(bind)。
简单思维实验
考虑到以上所有因素,
this
中的
referencedMethod
将是什么?
var referencedMethod = myObject.method;
referencedMethod();
正确! 将是全局上下文。这就是为什么如果您想与其他对象或代码共享方法,但仍保留原始所有者作为上下文的原因,则确实需要绑定(bind)或使函数与其所有者对象 bundle 在一起,以便您可以调用或应用。