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

Pass correct "this" context to setTimeout callback?

提问人:JamesBrownIsDead 提问时间:1/25/2010 最后编辑:NayukiJamesBrownIsDead 更新时间:7/3/2021 访问量:246451

问:

如何将上下文传递到?我想在 1000 毫秒后打电话。我该怎么做?setTimeoutthis.tip.destroy()this.options.destroyOnHide

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

当我尝试上述操作时,指的是窗口。this

JavaScript 回调 设置超时 绑定 jQuery 下划线 ecmascript-5 prototypejs node.js

评论

4赞 Sui Dream 10/30/2017
重复标志真的有效吗?这个问题其实早就被问过了。
1赞 Zibri 3/4/2018
if (this.options.destroyOnHide) { setTimeout(function() { this.tip.destroy() }.bind(this), 1000);

答:

478赞 Christian C. Salvadó 1/25/2010 #1

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

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

在一年前刚刚发布的 ES5 规范中,它引入了 bind 方法,这在最初的答案中没有提出,因为它还没有得到广泛支持,你需要 polyfills 才能使用它,但现在它无处不在:

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

该函数使用预填充的值创建一个新函数。bindthis

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

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

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

HTML5 早在 2011 年就对计时器进行了标准化,现在可以将参数传递给回调函数:

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

另请参阅:

评论

4赞 John K 1/25/2010
它有效。我用一个jsbin脚本测试了这个概念:jsbin.com/etise/7/edit
1赞 HoldOffHunger 10/21/2017
此代码涉及创建不必要的变量(具有函数范围);如果你正确地传入了这个函数,你就已经解决了这个问题,对于这种情况,对于map(),对于forEach()等,使用更少的代码,更少的CPU周期和更少的内存。参见:Misha Reyzlin的回答。this
240赞 Joel Purra 1/10/2012 #2

函数包装器有现成的快捷方式(语法糖),@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);
}

Prototype 中也实现了相同的功能(还有其他库吗?

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);
}

下划线.js,lodash

它在下划线 .js 和 lodash 中可用,如 1,2_.bind(...)

将函数绑定到对象,这意味着每当调用该函数时,的值 will 就是对象。(可选)将参数绑定到函数以预填充它们,也称为部分应用。this

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

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

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

.js

评论

0赞 aTei 7/12/2015
为什么不默认?我错过了什么吗?func.bind(context...)
0赞 Triynko 12/2/2015
每次调用它时,不断不断创建一个新函数(绑定会这样做)是否高效?我有一个搜索超时,每次按键后都会重置,似乎我应该将这个“绑定”方法缓存在某个地方以供重用。
0赞 Joel Purra 12/5/2015
@Triynko:我不认为绑定函数是一项昂贵的操作,但是如果您多次调用相同的绑定函数,您不妨保留一个引用:例如。var boundFn = fn.bind(this); boundFn(); boundFn();
32赞 Misha Reyzlin 2/16/2012 #3

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

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

因此,您可以这样做:

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

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

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

评论

1赞 Triynko 12/2/2015
当使用原型链创建类时,您的方法是原型方法...“bind”是唯一会改变方法中“this”的内容的东西。通过将参数传递给回调,您不会更改函数中的“this”,因此无法像任何其他原型方法那样使用“this”编写此类原型函数。这导致了不一致。Bind 是最接近我们实际想要的东西,闭包可以缓存在“this”中以获得更高的查找性能,而不必多次创建它。
2赞 Sam 10/21/2012 #4

如果使用下划线,则可以使用 .bind

例如

if (this.options.destroyOnHide) {
     setTimeout(_.bind(this.tip.destroy, this), 1000);
}
5赞 humkins 5/26/2014 #5

注意:这在 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);
0赞 Paul Razvan Berg 7/3/2021 #6

如果使用的是 TypeScript,则可以将函数作为参数传递,如下所示:

setTimeout(this.tip.destroy, 1000);

上下文将被分配,就像您将调用封装在 JavaScript 中的箭头函数中一样。this