简单的说...
为什么
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
工作完美,在指定的延迟后调用该函数,但是
setTimeout(playNote(currentaudio.id,noteTime), delay);
同时调用函数 playNote ?
(这些 setTimeout()s 在 for 循环中)
或者,如果我的解释太难读了,这两个函数之间有什么区别?
简单的说...
为什么
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
工作完美,在指定的延迟后调用该函数,但是
setTimeout(playNote(currentaudio.id,noteTime), delay);
同时调用函数 playNote ?
(这些 setTimeout()s 在 for 循环中)
或者,如果我的解释太难读了,这两个函数之间有什么区别?
您列出的第一种形式有效,因为它将在delay
. 使用eval()
通常不是一个好主意,所以你应该避免这种情况。
第二种方法不起作用,因为您立即使用函数调用 operator()
执行函数对象。最终发生的是,playNote
如果您使用 formplayNote(...)
会立即执行,因此在延迟结束时不会发生任何事情。
相反,您必须将匿名函数传递给 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); }
}
尝试这个。
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
不要使用字符串超时。这是有效的eval
,这是一件坏事。它之所以有效,是因为它正在将currentaudio.id
和转换noteTime
为自身的字符串表示并将其隐藏在代码中。这仅在这些值具有toString()
生成 JavaScript 文字语法的 s时才有效,该语法将重新创建该值,这Number
对于其他情况是正确的,但不适用于其他情况。
setTimeout(playNote(currentaudio.id, noteTime), delay);
那是一个函数调用。playNote
立即被调用,函数的返回结果(可能undefined
)被传递给setTimeout()
,而不是你想要的。
正如其他答案所提到的,您可以使用带有闭包的内联函数表达式来引用currentaudio
和noteTime
:
setTimeout(function() {
playNote(currentaudio.id, noteTime);
}, delay);
但是,如果你是在一个循环中和currentaudio
或noteTime
周围循环每一次都是不同的,你已经得到了闭合回路问题:同样的变量将在每一个超时而被引用的,所以他们调用时,你会得到相同的value 每次循环较早结束时留在变量中的值。
您可以使用另一个闭包解决此问题,为循环的每次迭代获取变量值的副本:
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)));
};
}
};
}
我真的在这个网站上创建了一个帐户来评论 Peter Ajtai 的答案(目前投票最高),结果发现您需要 50 位代表(无论是什么)才能发表评论,所以我将其作为答案,因为它可能值得指出出几件事情。
在他的回答中,他陈述了以下内容:
您还可以传递
setTimeout
引用,因为引用不会立即执行,但是您不能传递参数:setTimeout(playNote, delay);
这不是真的。在给出setTimeout
函数引用和延迟量之后,任何附加参数都被解析为被引用函数的参数。下面的内容比将函数调用包装在一个函数中要好。
setTimeout(playNote, delay, currentaudio.id, noteTime)
始终查阅文档。
也就是说,正如彼得指出的那样,如果您想改变每个 之间的延迟playNote()
,或者setInterval()
如果您希望每个 之间的延迟相同,则考虑使用递归函数将是一个好主意playNote()
。
还值得注意的是,如果要将i
for 循环的解析为setTimeout()
,则需要将其包装在一个函数中,详见此处。
由于第二个你告诉它来调用playNote功能第一,然后从它传递返回值的setTimeout。