我有一些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时不要传入一个字符串作为第一个参数,因为它有效地调用了对缓慢的“eval”函数的调用。

那么如何将参数传递给超时函数呢?通过使用闭包:

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

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

一些人建议在调用timeout函数时使用匿名函数:

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

语法是正确的。但是当settopic被调用时,也就是4秒之后,XHR对象可能就不一样了。因此,预绑定变量非常重要。

其他回答

请注意,topicId在错误消息中“未定义”的原因是在执行setTimeout时它作为一个局部变量存在,而在延迟调用postinsql时不存在。变量生存期尤其重要,特别是在尝试将“this”作为对象引用传递时。

我听说您可以将topicId作为第三个参数传递给setTimeout函数。没有给出太多细节,但我得到了足够的信息来让它工作,它在Safari中是成功的。我不知道他们所说的“毫秒误差”是什么意思。看看这里:

http://www.howtocreate.co.uk/tutorials/javascript/timers

我认为你想要:

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

//这是三个非常简单明了的答案:

function fun() {
    console.log(this.prop1, this.prop2, this.prop3);
}

let obj = { prop1: 'one', prop2: 'two', prop3: 'three' };

let bound = fun.bind(obj);

setTimeout(bound, 3000);

 // or

function funOut(par1, par2, par3) {

  return function() { 

    console.log(par1, par2, par3);

  }
};

setTimeout(funOut('one', 'two', 'three'), 5000);

 // or

let funny = function(a, b, c) { console.log(a, b, c); };

setTimeout(funny, 2000, 'hello', 'worldly', 'people');

我最近遇到了需要在循环中使用setTimeout的独特情况。理解这一点可以帮助您理解如何将参数传递给setTimeout。

方法1

使用forEach和Object。根据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

使用绑定:

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

方法五(ES6)

使用块作用域变量:

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

不过我还是推荐使用Object。在ES6中使用forEach键。

支持setTimeout参数的最简单的跨浏览器解决方案:

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

如果你不介意不支持ie9及以下版本:

setTimeout(postinsql, 4000, topicId);

https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout