简单地说......
为什么
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
完美地工作,在指定的延迟之后调用函数,但是
setTimeout(playNote(currentaudio.id,noteTime), delay);
同时调用函数playNote?
(这些setTimeout()s在for循环中)
或者,如果我的解释太难阅读,这两个函数之间有什么区别?
答案 0 :(得分:67)
您列出的第一个表单有效,因为它将在delay
结尾处评估字符串。使用eval()
通常不是一个好主意,所以你应该避免这种情况。
第二种方法不起作用,因为您立即使用 the function call operator ()
执行功能对象。最终发生的事情是playNote
如果您使用playNote(...)
形式会立即执行,因此在延迟结束时不会发生任何事情。
相反,您必须将匿名函数传递给setTimeout,因此正确的形式为:
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
请注意,您正在传递setTimeout
整个函数表达式,因此它将保留匿名函数并仅在延迟结束时执行它。
您也可以传递setTimeout
引用,因为引用不会立即执行,但是您无法传递参数:
setTimeout(playNote, delay);
注意:强>
对于重复活动,您可以使用 setInterval()
,然后将setInterval()
设置为变量,并使用该变量停止 clearInterval()
的间隔强>
您说您在setTimeout()
循环中使用for
。在许多情况下,最好在递归函数中使用setTimeout()
。这是因为在for
循环中,setTimeout()
中使用的变量将不是setTimeout()
开始时的变量,而是变量,因为它们是在函数延迟之后被解雇了。
只需使用递归函数来回避整个问题。
// Set original delay
var delay = 500;
// Call the function for the first time, to begin the recursion.
playNote(xxx, yyy);
// The recursive function
function playNote(theId, theTime)
{
// Do whatever has to be done
// ...
// Have the function call itself again after a delay, if necessary
// you can modify the arguments that you use here. As an
// example I add 20 to theTime each time. You can also modify
// the delay. I add 1/2 a second to the delay each time as an example.
// You can use a condition to continue or stop the recursion
delay += 500;
if (condition)
{ setTimeout(function() { playNote(theID, theTime + 20) }, delay); }
}
答案 1 :(得分:7)
试试这个。
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
答案 2 :(得分:4)
不要使用字符串超时。这是一个有效的eval
,这是一件坏事。它的工作原理是因为它将currentaudio.id
和noteTime
转换为自身的字符串表示并将其隐藏在代码中。只有这些值具有生成JavaScript文字语法的toString()
才能重新创建值时才有效,这对于Number
是正确的,但对于其他内容则不然。
setTimeout(playNote(currentaudio.id, noteTime), delay);
这是一个函数调用。立即调用playNote
并将函数的返回结果(可能是undefined
)传递给setTimeout()
,而不是您想要的。
正如其他答案所提到的,您可以使用带有闭包的内联函数表达式来引用currentaudio
和noteTime
:
setTimeout(function() {
playNote(currentaudio.id, noteTime);
}, delay);
但是,如果你处于一个循环中,每次循环时currentaudio
或noteTime
都不同,你就会遇到闭包循环问题:每个超时都会引用相同的变量,所以当它们被调用时,你每次都会获得相同的值,这是循环结束时变量中剩下的值。
您可以使用另一个闭包解决此问题,为循环的每次迭代获取变量值的副本:
setTimeout(function() {
return function(currentaudio, noteTime) {
playNote(currentaudio.id, noteTime);
};
}(currentaudio, noteTime), delay);
但现在这有点难看。更好的是Function#bind
,它将为您部分应用函数:
setTimeout(playNote.bind(window, currentaudio.id, noteTime), delay);
(window
用于设置函数内this
的值,这是此处不需要的bind()
的一项功能。)
然而,这是ECMAScript第五版功能,并非所有浏览器都支持。因此,如果你想使用它,你必须首先支持,例如:
// Make ECMA262-5 Function#bind work on older browsers
//
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
if (arguments.length<=1) {
return function() {
return that.apply(owner, arguments);
};
} else {
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
};
}
};
}
答案 3 :(得分:1)
因为第二个你告诉它调用playNote函数第一个然后将返回值传递给setTimeout。
答案 4 :(得分:0)
这可能有助于了解javascript何时执行代码以及何时等待执行某些操作:
let foo2 = function foo(bar=baz()){ console.log(bar); return bar()}
=>
语法的function关键字语法,得到类似的(but not identical)结果。foo2
baz
和bar
都没有,没有查找任何值,等等。但是,语法已经在函数内部进行了检查。foo
或foo2
传递给setTimeout
,则在超时后,它将调用该函数,就像执行foo()
一样。 (请注意,没有将参数传递给foo
。这是因为默认情况下,setTimeout
不会传递参数although it can,但是这些参数是在超时到期之前而不是在到期之前进行求值的。)bar
的默认值。 (如果我们通过一个参数就不会发生)bar
的默认参数时,第一个javascript将查找名为baz
的变量。如果找到一个,则尝试将其作为函数调用。如果可行,它将返回值保存到bar
。bar
,然后使用结果调用console.log。这不叫吧。但是,如果将其改为bar()
,则将先运行bar
,然后将bar()
的返回值传递给console.log
。请注意,javascript在调用之前甚至是在查找函数以查看它是否存在以及确实是一个函数之前,都会获取该函数的参数值。 >
bar
,然后尝试将其作为函数调用。如果可行,则返回值foo()
因此,不会立即调用函数体和默认参数,但其他所有函数都会被调用。同样,如果您进行函数调用(即()
),那么该函数也会立即执行。但是,不需要调用函数。不用括号,您就可以传递该函数并在以后调用它。不过,这样做的缺点是,您无法指定要使用该函数调用的参数。另外,javascript会在函数内用括号括起来 之前的所有内容,然后调用函数或查找存储在函数中的变量。
答案 5 :(得分:-1)
我在这个网站上创建了一个帐户来评论Peter Ajtai的回答(目前投票最高),但却发现你需要50个代表(无论是什么)才能发表评论,所以我会这样做这是一个答案,因为它可能值得指出几件事。
在他的回答中,他陈述了以下内容:
您也可以传递
setTimeout
引用,因为引用不会立即执行,但是您无法传递参数:setTimeout(playNote, delay);
这不是真的。在给予setTimeout
函数引用和延迟量之后,任何其他参数都被解析为引用函数的参数。以下将比在函数中包装函数调用更好。
setTimeout(playNote, delay, currentaudio.id, noteTime)
请务必查阅文档。
如上所述,正如彼得所指出的那样,如果你想改变每个playNote()
之间的延迟,或者如果你想要的话,可以考虑使用setInterval()
,那么递归函数会是一个好主意。每个playNote()
之间的延迟。
另外值得注意的是,如果要将for循环的i
解析为setTimeout()
,则需要将其包装在函数中,如详细here.