将正确的“this”上下文传递给 setTimeout 回调?
Posted
技术标签:
【中文标题】将正确的“this”上下文传递给 setTimeout 回调?【英文标题】:Pass correct "this" context to setTimeout callback? 【发布时间】:2011-01-08 23:17:17 【问题描述】:如何将上下文传递给setTimeout
?如果 this.options.destroyOnHide
在 1000 毫秒后,我想打电话给 this.tip.destroy()
。我该怎么做?
if (this.options.destroyOnHide)
setTimeout(function() this.tip.destroy() , 1000);
当我尝试上述方法时,this
指的是窗口。
【问题讨论】:
重复标志真的有效吗?这个问题其实早就问过了。 if (this.options.destroyOnHide) setTimeout(function() this.tip.destroy() .bind(this), 1000); 【参考方案1】:编辑: 总之,早在 2010 年问这个问题时,解决这个问题的最常见方法是保存对 setTimeout
函数调用的上下文的引用,因为setTimeout
执行函数,this
指向全局对象:
var that = this;
if (this.options.destroyOnHide)
setTimeout(function() that.tip.destroy() , 1000);
在那之前一年发布的 ES5 规范中,它引入了 bind
method,这在最初的答案中没有被建议,因为它还没有得到广泛的支持,你需要 polyfill 来使用它,但现在它是无处不在:
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 年的 standardized timers,现在您可以将参数传递给回调函数:
if (this.options.destroyOnHide)
setTimeout(function(that) that.tip.destroy() , 1000, this);
另见:
setTimeout - The 'this' problem【讨论】:
它有效。我用 jsbin 脚本测试了这个概念:jsbin.com/etise/7/edit 此代码涉及制作一个不必要的变量(具有函数范围);如果您正确地将this
传递给该函数,那么您就可以使用更少的代码、更少的 CPU 周期和更少的内存来解决这个问题,例如 map()、forEach() 等. ***见:Misha Reyzlin 的回答。【参考方案2】:
@CMS 回答的函数包装器有现成的快捷方式(语法糖)。 (下面假设你想要的上下文是this.tip
。)
ECMAScript 2015 (all common browsers and smartphones, Node.js 5.0.0+)
对于几乎所有的 javascript 开发(2020 年),您都可以使用 胖箭头函数,它们是 part of the ECMAScript 2015 (Harmony/ES6/ES2015) specification。
arrow function expression(也称为胖箭头函数)与函数表达式相比语法更短,并且在词法上绑定了
this
值[...]。
(param1, param2, ...rest) => statements
在你的情况下,试试这个:
if (this.options.destroyOnHide)
setTimeout(() => this.tip.destroy(); , 1000);
ECMAScript 5 (older browsers and smartphones, Node.js) 和 Prototype.js
如果您定位到browser compatible with ECMA-262, 5th edition (ECMAScript 5) 或Node.js(2020 年)意味着所有常见的浏览器以及旧版浏览器,您可以使用Function.prototype.bind
。您可以选择传递任何函数参数来创建partial functions。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
同样,在你的情况下,试试这个:
if (this.options.destroyOnHide)
setTimeout(this.tip.destroy.bind(this.tip), 1000);
implemented in Prototype(任何其他库?)也具有相同的功能。
Function.prototype.bind
can be implemented like this 如果您想要自定义向后兼容性(但请注意注释)。
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(...)
1,2
bind 将函数绑定到对象,这意味着每当调用该函数时,
this
的值将是该对象。或者,将参数绑定到函数以预先填充它们,也称为部分应用。
_.bind(function, object, [*arguments])
在你的情况下,试试这个:
if (this.options.destroyOnHide)
setTimeout(_.bind(this.tip.destroy, this.tip), 1000);
bindjqueryunderscore.jsecmascript-5prototypejsnode.js
【讨论】:
为什么不默认func.bind(context...)
?我错过了什么吗?
每次调用它时不断创建一个新函数(哪个 bind 会做)是否有效?我有一个搜索超时,每次按键后都会重置,看起来我应该在某处缓存这个“绑定”方法以供重用。
@Triynko:我不认为绑定函数是一项昂贵的操作,但如果您多次调用同一个绑定函数,您不妨保留一个引用:例如var boundFn = fn.bind(this); boundFn(); boundFn();
。跨度>
【参考方案3】:
在Internet Explorer以外的浏览器中,可以在延迟后将参数一起传递给函数:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
所以,你可以这样做:
var timeoutID = window.setTimeout(function (self)
console.log(self);
, 500, this);
就性能而言,这比范围查找(将this
缓存到超时/间隔表达式之外的变量中)然后创建闭包(通过使用$.proxy
或Function.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);
);
@*/
【讨论】:
当使用原型链创建一个类并且你的方法是原型方法时......'bind'是唯一会改变方法中'this'的东西。通过将参数传递给回调,您不会更改函数中的“this”,因此不能像任何其他原型方法一样在其中使用“this”来编写这样的原型函数。这会导致不一致。 Bind 是我们真正想要的最接近的东西,闭包可以缓存在 'this' 中以获得更高的查找性能,而不必多次创建它。【参考方案4】:注意:这在 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);
【讨论】:
【参考方案5】:如果您使用的是underscore
,则可以使用bind
。
例如
if (this.options.destroyOnHide)
setTimeout(_.bind(this.tip.destroy, this), 1000);
【讨论】:
【参考方案6】:如果您使用的是 TypeScript,则可以将函数作为参数传递,如下所示:
setTimeout(this.tip.destroy, 1000);
并且this
上下文将被分配,就像您将调用封装在 JavaScript 中的箭头函数中一样。
【讨论】:
以上是关于将正确的“this”上下文传递给 setTimeout 回调?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 React 的 Context 功能将 HTML5 Canvas 上下文传递给 this.props.children?
使用on()jQuery方法将'this'作为参数传递给事件处理程序