javascript - 如何将参数传递到setTimeout( ) 回调?

  显示原文与译文双语对照的内容

我有一些类似的JavaScript代码:


function statechangedPostQuestion()
{
//alert("statechangedPostQuestion");
 if (xmlhttp.readyState==4)
 {
 var topicId = xmlhttp.responseText;
 setTimeout("postinsql(topicId)",4000);
 }
}

function postinsql(topicId)
{
//alert(topicId);
}

我得到了一个错误,topicId 没有定义一切在使用 setTimeout() 函数之前都是工作的。

我希望在一段时间后调用我的postinsql(topicId) 函数。 我该怎么做?

时间:


setTimeout(function() {
 postinsql(topicId);
}, 4000)

你需要将匿名函数作为参数而不是字符串提供,后者的方法甚至不应该按照ECMAScript规范工作,但是浏览器只是宽松的。 这是妥善解决,不要依赖传递一个字符串作为'函数'使用 setTimeout() 或者 setInterval() 时,它是慢的,因为它必须被评估,它是不正确的。

在进行了一些研究和测试之后,唯一正确的实现是:


setTimeout(yourFunctionReference, 4000, param1, param2, paramN);

setTimeout将把所有额外参数传递给函数,这样它们就可以在那里处理了。

匿名函数可以为非常基本的东西工作,但是在一个对象的实例中,你不能让它工作。 任何匿名函数都将更改"这个"指向窗口,因此你将丢失对象引用。

这是一个已经有"正确"答案的旧问题,但我想我会提到另一个没有人提到的方法。 这是从优秀的下划线库复制和粘贴的:


_.delay = function(func, wait) {
 var args = slice.call(arguments, 2);
 return setTimeout(function(){ return func.apply(null, args); }, wait);
};

你可以通过尽可能多的参数要调用的函数settimeout和还有一个额外的好处( 通常是奖金) 传递给函数的参数的值被冻结当你叫settimeout,所以如果他们改变价值之间在某些时候当 setTimeout() 叫做超时时,嗯。 这并不让hideously感到沮丧:)

这里是一个小提琴,你可以看到我的意思- http://jsfiddle.net/thedavidmeister/7t2bV/

我最近遇到的特殊情况需要使用 setTimeout循环。 对于这个问题,任何人都想知道这个,下面是一些如何实现这个的例子。

方法 1

使用 bind:


var testObject = {
 prop1: 'test1',
 prop2: 'test2',
 prop3: 'test3'
};

var i = 0;
for (var propertyName in testObject) {
 setTimeout(function(propertyName) {
 console.log(testObject[propertyName]);
 }.bind(this, propertyName), i++ * 1000);
}

JSFiddle: http://jsfiddle.net/MsBkW/

方法 2

使用 forEachObject.keyssukima建议 :


Object.keys(testObject).forEach(function(propertyName, i) {
 setTimeout(function() {
 console.log(testObject[propertyName]);
 }, i * 1000);
});

方法 3

或者如果你不能使用 forEach 或者 bind,请使用 life:


var i = 0;
for (var propertyName in testObject) {
 setTimeout((function(propertyName) {
 return function() {
 console.log(testObject[propertyName]);
 };
 })(propertyName), i++ * 1000);
}

方法 4

但是如果你不关心 IE,那么你可以使用建议的:


var i = 0;
for (var propertyName in testObject) {
 setTimeout(function(propertyName) {
 console.log(testObject[propertyName]);
 }, i++ * 1000, propertyName);
}

有些答案是正确但却很复杂。

我将在 4年后再次回答这个问题,因为我仍然遇到过于复杂的代码来解决这个问题。 有一个优雅的解决方案。

首先,在调用setTimeout时不要作为第一个参数传入字符串,因为它有效调用了对慢速"评估"函数的调用。

那么我们如何传递一个参数到超时函数? 通过使用闭包:


settopic=function(topicid){
 setTimeout(function(){
//thanks to closure, topicid is visible here
 postinsql(topicid);
 },4000);
}

...
if (xhr.readyState==4){
 settopic(xhr.responseText);
}

有人建议在调用超时函数时使用匿名函数:


if (xhr.readyState==4){
 setTimeout(function(){
 settopic(xhr.responseText);
 },4000);
}

语法可行。但在调用setTopic时,换句话说,4秒后,XHR对象可能不相同。 因此,pre-bind对变量

我知道它是旧的,但我想把我的( 首选) 味道添加到这个。

我认为实现这一点的一个很好的方法是将 topicId 传递给函数,后者又使用参数来在内部引用主题标识。 即使在外部的topicId 很快被更改,这个值也不会改变。


var topicId = xmlhttp.responseText;
var fDelayed = function(tid) {
 return function() {
 postinsql(tid);
 };
}
setTimeout(fDelayed(topicId),4000);

或者简称:


var topicId = xmlhttp.responseText;
setTimeout(function(tid) {
 return function() { postinsql(tid); };
}(topicId), 4000);

替换


 setTimeout("postinsql(topicId)", 4000);

打开方式


 setTimeout("postinsql(" + topicId +")", 4000);

或者更好的是,用匿名函数替换字符串表达式


 setTimeout(function () { postinsql(topicId); }, 4000);

编辑:

brownstone的评论是不正确的,这将会按预期工作,可以由运行在firebug控制台


(function() {
 function postinsql(id) {
 console.log(id);
 }
 var topicId = 3
 window.setTimeout("postinsql(" + topicId +")",4000);//outputs 3 after 4 seconds
})();

请注意,我在与他人agreeance你应该避免将一个字符串 setTimeout 这将称为 eval() 弦上的,而是通过一个函数。

...