传递正确的“this”;上下文setTimeout回调?

如何将上下文传递到setTimeout?如果this.options.destroyOnHide在1000毫秒后,我想调用this.tip.destroy()。我该怎么做呢?

if (this.options.destroyOnHide) {
setTimeout(function() { this.tip.destroy() }, 1000);
}

当我尝试上面的时候,this指的是窗口。

235835 次浏览

总之,早在2010年,当这个问题被问到时,解决这个问题最常见的方法是保存对setTimeout函数调用的上下文的引用,因为setTimeout执行函数时,this指向全局对象:

var that = this;
if (this.options.destroyOnHide) {
setTimeout(function(){ that.tip.destroy() }, 1000);
}

在一年前刚刚发布的ES5规范中,它引入了bind方法,这在最初的答案中没有被建议,因为它还没有被广泛支持,你需要填充程序来使用它,但现在它无处不在:

if (this.options.destroyOnHide) {
setTimeout(function(){ this.tip.destroy() }.bind(this), 1000);
}

bind函数创建了一个预先填充this值的新函数。

现在在现代JS中,这正是箭头函数在ES6中解决的问题:

if (this.options.destroyOnHide) {
setTimeout(() => { this.tip.destroy() }, 1000);
}

箭头函数没有自己的this值,当你访问它时,你是在访问外围词法作用域的this值。

HTML5也在2011年标准化的计时器,现在你可以将参数传递给回调函数:

if (this.options.destroyOnHide) {
setTimeout(function(that){ that.tip.destroy() }, 1000, this);
}

参见:

对于@CMS回答的函数包装器,有现成的快捷方式(语法糖)。(下面假设你想要的上下文是this.tip。)


ECMAScript 2015 (所有常见的浏览器和智能手机, Node.js 5.0.0+)

对于几乎所有的javascript开发(在2020年),您都可以使用胖箭头函数,它们是ECMAScript 2015 (Harmony/ES6/ES2015)规范的一部分

与函数表达式相比,箭头函数表达式(也称为胖箭头函数)具有更短的语法,并且在词法上绑定this值[…]。

(param1, param2, ...rest) => { statements }

在你的情况下,试试这个:

if (this.options.destroyOnHide) {
setTimeout(() => { this.tip.destroy(); }, 1000);
}

ECMAScript 5 (老式浏览器和智能手机, Node.js)和Prototype.js

如果你的目标是浏览器兼容ECMA-262,第5版(ECMAScript 5)node . js,这(在2020年)意味着所有常用浏览器以及旧浏览器,你可以使用Function.prototype.bind。你可以选择传递任何函数参数来创建部分功能

fun.bind(thisArg[, arg1[, arg2[, ...]]])

同样,在你的情况下,试试这个:

if (this.options.destroyOnHide) {
setTimeout(this.tip.destroy.bind(this.tip), 1000);
}

同样的功能也被在原型中实现(还有其他库吗?)

Function.prototype.bind可以像这样实现如果你想自定义向后兼容性(但请观察注释)。


jQuery

如果你已经在使用jQuery 1.4+,有一个现成的函数用于显式设置函数的this上下文。

jQuery.proxy ():接受一个函数并返回一个新函数,该函数将始终具有特定的上下文。

$.proxy(function, context[, additionalArguments])

在你的情况下,试试这个:

if (this.options.destroyOnHide) {
setTimeout($.proxy(this.tip.destroy, this.tip), 1000);
}

Underscore.js, lodash

它可以在Underscore.js和lodash中以_.bind(...)12的形式使用

<强> < /绑定强>将函数绑定到对象,这意味着无论何时调用该函数,this的值都将是该对象。可选地,将参数绑定到函数以预填充它们,也称为部分应用程序。

_.bind(function, object, [*arguments])

在你的情况下,试试这个:

if (this.options.destroyOnHide) {
setTimeout(_.bind(this.tip.destroy, this.tip), 1000);
}

__abc0 __abc1 __abc2 __abc3 __abc4 __abc5

在ie以外的浏览器中,你可以在延迟后将参数一起传递给函数:

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

你可以这样做:

var timeoutID = window.setTimeout(function (self) {
console.log(self);
}, 500, this);

就性能而言,这比作用域查找(将this缓存到timeout / interval表达式之外的变量中),然后创建闭包(通过使用$.proxyFunction.prototype.bind)更好。

使它在ie中工作的代码来自Webreflection:

/*@cc_on
(function (modifierFn) {
// you have to invoke it as `window`'s property so, `window.setTimeout`
window.setTimeout = modifierFn(window.setTimeout);
window.setInterval = modifierFn(window.setInterval);
})(function (originalTimerFn) {
return function (callback, timeout){
var args = [].slice.call(arguments, 2);
return originalTimerFn(function () {
callback.apply(this, args)
}, timeout);
}
});
@*/

如果你正在使用underscore,你可以使用bind

如。

if (this.options.destroyOnHide) {
setTimeout(_.bind(this.tip.destroy, this), 1000);
}

注意:这将不能在IE中工作

var ob = {
p: "ob.p"
}


var p = "window.p";


setTimeout(function(){
console.log(this.p); // will print "window.p"
},1000);


setTimeout(function(){
console.log(this.p); // will print "ob.p"
}.bind(ob),1000);

如果你使用的是TypeScript,你可以将函数作为参数传递,就像这样:

setTimeout(this.tip.destroy, 1000);

并且this上下文将被赋值,就好像你在JavaScript的箭头函数中封装了调用一样。