在JavaScript中使用setTimeout()和setInterval()时如何调用函数

5
如果我在setTimeout()和setInterval()中只使用函数名而不加括号调用命名函数,则会按预期执行。但是如果我使用括号调用同一个函数,则可能立即执行该函数或出现错误。
我希望能够更深入地了解这个问题,比网上找到的内容更详细一些。请问是否可以向我解释为什么会出现这种情况?
var func = function(){
    console.log("Bowties are cool.");
}

setTimeout(func(), 1500);
// Prints "Bowties are cool." immediately

setInterval(func(), 1500);
// Throws an error

setInterval(func, 1500);
// Works as expected

setTimeout(console.log("Bowties are cool."),1500);
// This method has the same result as "setTimeout(func(), 1500)".

1
() 运算符会导致函数被调用,因此当您在 setTimeout() 的调用中使用 func() 时,函数将被调用并且 结果(返回值)将传递给 setTimeout() - Pointy
你应该传递一个函数,而不是函数的结果(除非它返回一个函数)- 所以它不能正常工作,这很让人惊讶吗? - Dominic
函数在 JavaScript 中是一等公民。您可以像任何其他变量一样传递它们。当您仅使用函数名称时,您正在传递对该函数的引用。当您使用括号 () 时,您正在调用该函数并传递该调用的结果 - Matt Burland
3个回答

6
您必须向setTimeout()setInterval()都传递一个函数引用。这意味着您传递一个函数名而没有在其后面加上(),或者传递一个匿名函数。

当您在函数名后面加上(),比如func(),则会立即执行该函数,然后将返回结果传递给setInterval()setTimeout()。除非该函数本身返回另一个函数引用,否则它永远不会实现您想要的功能。这是JavaScript程序员常犯的错误之一(我学习该语言时也犯过同样的错误)。

因此,正确的代码是:

setTimeout(func, 1500);
setInterval(func, 1500);

如果你知道其他使用指针的语言,你可以将带有()的函数名视为指向该函数的指针,在Javascript中这就是函数引用,当你想让函数稍后执行而不是立即执行时,就会将其传递给函数。
当你错误地做了这件事:
setTimeout(func(), 1500);
setInterval(func(), 1500);

它立即执行你的func(),然后将返回结果传递给setTimeout()setInterval()。由于你的func()没有返回任何内容,实际上你正在做这件事:

func();
setTimeout(undefined, 1500);

你观察到的是发生了什么,但不是你想要的。


此外,如果你想要执行一个特定的函数调用,比如 console.log("Bowties are cool."),那么你可以将它包装在另一个函数里面,像这样:

setTimeout(function() {
    console.log("Bowties are cool.")
}, 1500);

所以,你再次将函数引用传递给setTimeout(),该函数可以在稍后执行,而不是立即执行,这就是你之前所做的。

当执行此代码时,JavaScript引擎会采取哪种逻辑路径?它首先看到什么,参数以何种顺序放置在堆栈中?希望这个问题有意义。 - sufuninja
@sufuninja:如果您有其他问题,请提出另一个问题并解释您的意思。 - Matt Burland
@sufuninja - 我在我的回答中给了你一个等效执行顺序的例子。当你将func()作为参数时,它首先被评估,然后将其结果作为参数传递。 - jfriend00

0

setTimeout和setInterval期望传递给它们一个函数引用。你不应该在setTimeout和setInterval调用中调用该函数。

错误的做法(不要这样做):

setTimeout(func(), 1500);
setInterval(func(), 1500);
setTimeout(console.log("Bowties are cool."), 1500);
setInterval(console.log("Bowties are cool."), 1500);

正确:

setTimeout(func, 1500);
setInterval(func, 1500);
setTimeout(function() {
    console.log("Bowties are cool.");
}, 1500);
setInterval(function() {
    console.log("Bowties are cool.");
}, 1500);

注意我如何将console.log包装在匿名函数中,以便能够与setInterval和setTimeout一起使用。 我无法将console.log传递给setInterval和setTimeout函数,因为它需要参数“Bowties are cool。”。 将其放入匿名函数中即可解决此问题。

0

当您在已定义的函数后添加括号时,实际上是调用该函数。

但是,当您将一个函数作为参数传递到另一个函数中时,您不希望调用该函数,而只是想将对此函数的引用作为参数传递(并且传递的函数将在需要时可供调用)。

有关回调函数的更多信息,请参阅这里


1
谢谢提供链接。我之前读过这篇文章,但是概念还没有完全理解。 - sufuninja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接