提问人:quantum rookie 提问时间:9/27/2010 最后编辑:Peter Ajtaiquantum rookie 更新时间:10/1/2022 访问量:110599
使用 setTimeout() 调用函数
Calling functions with setTimeout()
问:
简单地说......
为什么
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
工作完美,在指定的延迟后调用函数,但
setTimeout(playNote(currentaudio.id,noteTime), delay);
同时调用函数 playNote?
(这些 setTimeout() 在 for 循环中)
或者,如果我的解释太难读,这两个函数之间有什么区别?
答:
试试这个。
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
因为第二个你告诉它先调用 playNote 函数,然后将返回值从它传递给 setTimeout。
您列出的第一种形式有效,因为它将计算 末尾的字符串。使用通常不是一个好主意,所以你应该避免这种情况。delay
eval()
第二种方法不起作用,因为您立即使用函数调用运算符 ()
执行函数对象。最终发生的事情是,如果您使用表单,则会立即执行,因此在延迟结束时不会发生任何事情。playNote
playNote(...)
相反,您必须将匿名函数传递给 setTimeout,因此正确的形式是:
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
请注意,您传递的是整个函数表达式,因此它将保留匿名函数,并且仅在延迟结束时执行它。setTimeout
你也可以传递一个引用,因为引用不会立即执行,但你无法传递参数:setTimeout
setTimeout(playNote, delay);
注意:
对于重复事件,您可以使用 setInterval(),也可以设置为变量,然后通过 clearInterval(
)
使用该变量来停止间隔。setInterval()
你说你循环使用。在许多情况下,最好在递归函数中使用。这是因为在循环中,中使用的变量将不是开始时的变量,而是函数触发时延迟后的变量。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); }
}
评论
playNote
不要使用字符串超时。这是有效的,这是一件坏事。它之所以有效,是因为它正在转换和转换为自己的字符串表示形式并将其隐藏在代码中。这只有在这些值具有生成 JavaScript 文本语法的 s 时才有效,该语法将重新创建该值,这对于其他值是正确的,但对于其他很多值来说都不是。eval
currentaudio.id
noteTime
toString()
Number
setTimeout(playNote(currentaudio.id, noteTime), delay);
这是一个函数调用。 立即调用,函数的返回结果(可能)被传递给 ,而不是你想要的。playNote
undefined
setTimeout()
正如其他答案所提到的,您可以使用带有 reference 闭包的内联函数表达式,并且: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)));
};
}
};
}
我在这个网站上创建了一个帐户来评论 Peter Ajtai 的答案(目前得票最高),结果发现您需要 50 个代表(不管是什么)才能发表评论,所以我会把它作为一个答案,因为它可能值得指出几件事。
在他的回答中,他指出:
你也可以传递一个引用,因为引用不会立即执行,但你无法传递参数:
setTimeout
setTimeout(playNote, delay);
事实并非如此。在给出函数引用和延迟量后,任何其他参数都将被解析为引用函数的参数。下面比将函数调用包装在函数中要好。setTimeout
setTimeout(playNote, delay, currentaudio.id, noteTime)
始终查阅文档。
也就是说,正如 Peter 所指出的,如果您想改变每个函数之间的延迟,或者如果您希望每个函数之间存在相同的延迟,请考虑使用递归函数。playNote()
setInterval()
playNote()
另外值得注意的是,如果你想将 for 循环解析为 ,你需要将其包装在一个函数中,详见此处。i
setTimeout()
了解 javascript 何时执行代码以及何时等待执行某些内容可能会有所帮助:
let foo2 = function foo(bar=baz()){ console.log(bar); return bar()}
- javascript 执行的第一件事是函数构造函数,并创建一个函数对象。您可以使用函数关键字语法或语法,您将获得相似(但不完全相同)的结果。
=>
- 然后将刚刚创建的函数分配给变量
foo2
- 此时没有运行任何其他函数:没有调用其他函数(既不是也不是,没有查找值等。但是,语法已在函数内部进行了检查。
baz
bar
- 如果要在超时后传递或 to,它将调用该函数,就像您调用函数一样。(请注意,没有将任何参数传递给 。这是因为默认情况下不会传递参数,尽管它可以,但这些参数在超时到期之前进行评估,而不是在超时到期时进行评估。
foo
foo2
setTimeout
foo()
foo
setTimeout
- 调用 foo 后,将计算默认参数。由于我们在未传递参数的情况下调用了 foo,因此将计算默认值。(如果我们通过一个论点,这就不会发生)
bar
- 在计算 的默认参数时,首先 javascript 会查找一个名为 的变量。如果它找到一个,它就会尝试将其作为函数调用。如果可行,则将返回值保存到 。
bar
baz
bar
- 现在计算函数的主体:
- Javascript 查找变量,然后使用结果调用 console.log。这不调用栏。但是,如果它被调用为 ,则将首先运行,然后将返回值传递给 。请注意,javascript 在调用函数之前,甚至在查找函数以查看它是否存在并且确实是一个函数之前,都会获取它正在调用的函数的参数值。
bar
bar()
bar
bar()
console.log
- Javascript 再次查找 ,然后尝试将其作为函数调用。如果可行,则返回该值作为
bar
foo()
因此,函数体和默认参数不会立即调用,但其他一切都会立即调用。同样,如果您执行函数调用(即 ),则该函数也会立即执行。但是,不需要调用函数。省略括号将允许您传递该函数并在以后调用它。但是,这样做的缺点是,您无法指定要调用函数的参数。此外,javascript 在调用函数或查找存储函数的变量之前,会在函数括号内执行所有操作。()
评论
ctr-k