关于javascript:将正确的“this”上下文传递给setTimeout回调?

Pass correct “this” context to setTimeout callback?

本问题已经有最佳答案,请猛点这里访问。

如何将上下文传递到setTimeout中?我想打电话给this.tip.destroy(),如果1000毫秒后this.options.destroyOnHide,我该怎么做?

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

当我尝试上述方法时,this指的是窗口。


您需要保存对执行setTimeout函数调用的上下文的引用,因为setTimeout使用指向全局对象的this执行函数:

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

您可以很容易地证明setTimeout通过以下方式将this设置为全局对象:

1
2
3
4
5
6
(function () {
  alert(this); // alerts hello
  setTimeout(function(){
    alert(this == window); // true
  }, 1000);
}).call("hello");

参见:

  • 设置超时-这个问题


有现成的快捷方式(语法糖)到用回答的函数包装器@cms。(假设您需要的上下文是this.tip)。

ecmascript 5(当前浏览器,node.js)和prototype.js

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

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

同样,在您的情况下,请尝试以下操作:

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

同样的功能也在原型中实现(其他库?).

如果您希望自定义向后兼容性(但请遵守注释),可以这样实现Function.prototype.bind

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

对于尖端开发(2015),您可以使用fat arrow函数,这是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 this value [...].

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

在您的情况下,请尝试以下操作:

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

JQuery

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

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

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

在您的情况下,请尝试以下操作:

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

下划线.js,lodash

它可以在underline.js和lodash中使用,如_.bind(...)1,2。

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

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

在您的情况下,请尝试以下操作:

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

bind jquery underline.js ecmascript-5 prototypejs node.js


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

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

所以,你可以这样做:

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

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

通过WebReflection使其在IES中工作的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*@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);
    }
});
@*/


注意:这在IE中不起作用

1
2
3
4
5
6
7
8
9
10
11
12
13
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);

如果您使用的是underscore,则可以使用bind

例如。

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