我有一些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()
它更慢,因为它必须被评估并且它不正确.
更新:
正如Hobblin在对该问题的评论中所说,现在你可以将参数传递给setTimeout中的函数 Function.prototype.bind()
例:
setTimeout(postinsql.bind(null, topicId), 4000);
在现代浏览器中,"setTimeout"接收第三个参数,该参数作为参数发送到计时器末尾的内部函数.
例:
var hello = "Hello World";
setTimeout(alert, 1000, hello);
在做了一些研究和测试之后,唯一正确的实现是:
setTimeout(yourFunctionReference, 4000, param1, param2, paramN);
setTimeout会将所有额外参数传递给您的函数,以便在那里处理它们.
匿名函数可以用于非常基本的东西,但是在你必须使用"this"的对象的实例中,没有办法让它工作.任何匿名函数都会将"this"更改为指向窗口,因此您将丢失对象引用.
这是一个非常古老的问题,已有"正确"的答案,但我想我会提到另一种方法,没有人在这里提到过.这是从优秀的下划线库中复制和粘贴的:
_.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); };
您可以将所需的参数传递给setTimeout调用的函数,并作为额外的奖励(通常是奖金),当您调用setTimeout时,传递给函数的参数值将被冻结,因此如果它们更改了值在调用setTimeout()和超时之间的某个时刻,好吧......那不再那么令人沮丧了:)
这是一个小提琴,你可以看到我的意思.
我最近遇到了特殊情况需要来使用的setTimeout
在循环中.理解这一点可以帮助您了解如何传递参数setTimeout
.
方法1
使用forEach
和Object.keys
根据Sukima的建议:
var testObject = { prop1: 'test1', prop2: 'test2', prop3: 'test3' }; Object.keys(testObject).forEach(function(propertyName, i) { setTimeout(function() { console.log(testObject[propertyName]); }, i * 1000); });
我推荐这种方法.
方法2
使用 bind
:
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/
方法3
或者,如果您不能使用forEach
或bind
使用IIFE:
var i = 0; for (var propertyName in testObject) { setTimeout((function(propertyName) { return function() { console.log(testObject[propertyName]); }; })(propertyName), i++ * 1000); }
方法4
但如果你不关心IE <10,那么你可以使用Fabio的建议:
var i = 0; for (var propertyName in testObject) { setTimeout(function(propertyName) { console.log(testObject[propertyName]); }, i++ * 1000, propertyName); }
方法5(ES6)
使用块范围变量:
let i = 0; for (let propertyName in testObject) { setTimeout(() => console.log(testObject[propertyName]), i++ * 1000); }
虽然我还是会建议使用Object.keys
与forEach
在ES6.
霍布林已就这个问题对此进行了评论,但这应该是一个答案!
使用Function.prototype.bind()
是最干净,最灵活的方法(能够设置this
上下文的额外好处):
setTimeout(postinsql.bind(null, topicId), 4000);
有关更多信息,请参阅以下MDN链接:
https
://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function /绑定#With_setTimeout
一些答案是正确的,但令人费解.
4年后我再次回答这个问题,因为我仍然遇到过于复杂的代码来解决这个问题.有一个优雅的解决方案.
首先,在调用setTimeout时不要传入一个字符串作为第一个参数,因为它有效地调用了对慢速"eval"函数的调用.
那么我们如何将参数传递给超时函数呢?通过使用闭包:
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对象可能不一样.因此,预先绑定变量很重要.
我的答案:
setTimeout((function(topicId) { return function() { postinsql(topicId); }; })(topicId), 4000);
说明:
创建的匿名函数返回另一个匿名函数.此函数可以访问最初传递的函数
topicId
,因此不会出错.第一个匿名函数立即被调用,传入topicId
,因此具有延迟的注册函数可以topicId
在调用时通过闭包访问.
要么
这基本上转换为:
setTimeout(function() { postinsql(topicId); // topicId inside higher scope (passed to returning function) }, 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 })();
请注意,我同意其他人应避免传递字符串,setTimeout
因为这会调用eval()
字符串而不是传递函数.
支持setTimeout中参数的最简单的跨浏览器解决方案:
setTimeout(function() { postinsql(topicId); }, 4000)
如果您不介意不支持IE 9及更低版本:
setTimeout(postinsql, 4000, topicId);
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout