我有一些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回调函数,如下所示:

setTimeout(函数,毫秒,param1, param2,…)

eg.

function myFunction() {
  setTimeout(alertMsg, 3000, "Hello");
}

function alertMsg(message) {
    alert(message)
}

其他回答

这适用于所有浏览器(IE是一个奇怪的)

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

您需要将匿名函数作为参数而不是字符串提供,根据ECMAScript规范,后一种方法甚至不应该工作,但浏览器只是宽容。这是正确的解决方案,当使用setTimeout()或setInterval()时,不要依赖于传递字符串作为“函数”,它更慢,因为它必须被计算,这是不正确的。

更新:

正如Hobblin在他对问题的评论中所说,现在你可以使用function .prototype.bind()将参数传递给setTimeout内的函数。

例子:

setTimeout(postinsql.bind(null, topicId), 4000);

取代

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

with

 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(),而不是传递一个函数。

一般来说,如果需要将函数作为带有特定参数的回调函数传递,则可以使用高阶函数。这在ES6中是非常优雅的:

const someFunction = (params) => () => {
  //do whatever
};

setTimeout(someFunction(params), 1000);

或者如果someFunction是一阶的:

setTimeout(() => someFunction(params), 1000); 

我最近遇到了需要在循环中使用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键。