Javascript 将正确的“this”上下文传递给 setTimeout 回调?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2130241/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 22:52:07  来源:igfitidea点击:

Pass correct "this" context to setTimeout callback?

javascriptcallbackthissettimeout

提问by JamesBrownIsDead

How do I pass context into setTimeout? I want to call this.tip.destroy()if this.options.destroyOnHideafter 1000 ms. How can I do that?

我如何将上下文传递给setTimeout?我想打电话this.tip.destroy(),如果this.options.destroyOnHide在1000毫秒。我怎样才能做到这一点?

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

When I try the above, thisrefers to the window.

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

回答by CMS

EDIT:In summary, back in 2010 when this question was asked the most common way to solve this problem was to save a reference to the context where the setTimeoutfunction call is made, because setTimeoutexecutes the function with thispointing to the global object:

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

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

In the ES5 spec, just released a year before that time, it introduced the bindmethod, this wasn't suggested in the original answer because it wasn't yet widely supported and you needed polyfills to use it but now it's everywhere:

在一年前刚刚发布的 ES5 规范中,它引入了bindmethod,原始答案中没有建议这样做,因为它尚未得到广泛支持,您需要使用 polyfills 才能使用它,但现在它无处不在:

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

The bindfunction creates a new function with the thisvalue pre-filled.

bind函数创建一个具有this预填充值的新函数。

Now in modern JS, this is exactly the problem arrow functions solve in ES6:

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

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

Arrow functions do not have a thisvalue of its own, when you access it, you are accessing the thisvalue of the enclosing lexical scope.

箭头函数没有自己的this值,当您访问它时,您正在访问this封闭词法范围的值。

HTML5 also standardized timersback in 2011, and you can pass now arguments to the callback function:

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

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

See also:

也可以看看:

回答by Joel Purra

There are ready-made shortcuts (syntactic sugar) to the function wrapper @CMS answered with. (Below assuming that the context you want is this.tip.)

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



ECMAScript 5(current browsers, Node.js) and Prototype.js

ECMAScript 5当前浏览器,Node.js)和 Prototype.js

If you target browser compatible with ECMA-262, 5th edition (ECMAScript 5)or Node.js, you could use Function.prototype.bind. You can optionally pass any function arguments to create partial functions.

如果您的目标浏览器与 ECMA-262, 5th edition (ECMAScript 5)Node.js兼容,您可以使用Function.prototype.bind. 您可以选择传递任何函数参数来创建部分函数

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

Again, in your case, try this:

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

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

The same functionality has also been implemented in Prototype(any other libraries?).

Prototype(任何其他库?)中也实现了相同的功能。

Function.prototype.bindcan be implemented like thisif you want custom backwards compatibility (but please observe the notes).

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



ECMAScript 2015(some browsers, Node.js 5.0.0+)

ECMAScript 2015某些浏览器,Node.js 5.0.0+)

For cutting-edge development (2015) you can use fat arrow functions, which are part of the ECMAScript 2015 (Harmony/ES6/ES2015) specification(examples).

对于前沿开发(2015),您可以使用胖箭头函数,它是ECMAScript 2015(Harmony/ES6/ES2015)规范示例)的一部分。

An arrow function expression(also known as fat arrow function) has a shorter syntax compared to function expressions and lexically binds the thisvalue [...].

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

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

In your case, try this:

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

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


jQuery

jQuery

If you are already using jQuery 1.4+, there's a ready-made function for explicitly setting the thiscontext of a function.

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

jQuery.proxy(): Takes a function and returns a new one that will always have a particular context.

jQuery.proxy():接受一个函数并返回一个总是具有特定上下文的新函数。

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

In your case, try this:

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

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


Underscore.js, lodash

下划线.js, lodash

It's available in Underscore.js, as well as lodash, as _.bind(...)1,2

它在 Underscore.js 和 lodash 中可用,如_.bind(...)12

bindBind a function to an object, meaning that whenever the function is called, the value of thiswill be the object. Optionally, bind arguments to the function to pre-fill them, also known as partial application.

bind 将一个函数绑定到一个对象,这意味着无论何时调用该函数,其值都this将是该对象。或者,将参数绑定到函数以预填充它们,也称为部分应用。

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

In your case, try this:

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

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


bindjqueryunderscore.jsecmascript-5prototypejsnode.js

绑定的jquery underscore.js ECMAScript的5 prototypejs的node.js

回答by Misha Reyzlin

In browsers other than Internet Explorer, you can pass parameters to the function together after the delay:

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

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

So, you can do this:

所以,你可以这样做:

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

This is better in terms of performance than a scope lookup (caching thisinto a variable outside of the timeout / interval expression), and then creating a closure (by using $.proxyor Function.prototype.bind).

这在性能方面比范围查找(缓存this到超时/间隔表达式之外的变量中),然后创建闭包(通过使用$.proxyFunction.prototype.bind)更好。

The code to make it work in IEs from Webreflection:

使其在来自Webreflection 的IE 中工作的代码:

/*@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);
    }
});
@*/

回答by gumkins

NOTE: This won't work in IE

注意:这在 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);

回答by Sam

If you're using underscore, you can use bind.

如果您正在使用underscore,则可以使用bind.

E.g.

例如

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