Javascript 使用setTimeout()调用函数

Javascript 使用setTimeout()调用函数,javascript,settimeout,Javascript,Settimeout,简单地说 为什么 setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay); 工作正常,在指定的延迟后调用函数,但是 setTimeout(playNote(currentaudio.id,noteTime), delay); 是否同时调用函数playNote (这些setTimeout()位于for循环中) 或者,如果我的解释太难理解,那么这两个函数之间的区别是什么?试试这个 setTimeout(function()

简单地说

为什么

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(playNote, delay);
setTimeout(playNote, delay);

注意:

对于重复事件,您可以使用,您可以将
setInterval()
设置为变量,并使用该变量停止间隔

您说您在
for
循环中使用
setTimeout()
。在许多情况下,最好在递归函数中使用
setTimeout()
。这是因为在
for
循环中,
setTimeout()
中使用的变量将不是
setTimeout()
开始时的变量,而是在函数启动延迟后的变量

只需使用递归函数就可以避开整个问题

使用递归处理可变延迟时间:
不要使用字符串超时。它是有效的
评估
,这是一件坏事。它之所以有效,是因为它正在将
currentaudio.id
noteTime
转换为它们自身的字符串表示形式,并将其隐藏在代码中。这只适用于那些值具有生成JavaScript文本语法的
toString()
s以重新创建该值的情况,这对于
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);
但现在这有点难看了。更好的是
函数#bind
,它将为您部分应用函数:

setTimeout(playNote.bind(window, currentaudio.id, noteTime), delay);
窗口
用于在函数内部设置
的值,这是
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)));
            };
        }
    };
}
//使ECMA262-5功能#绑定在旧浏览器上工作
//
if(!('bind'在Function.prototype中)){
Function.prototype.bind=函数(所有者){
var=这个;

如果(arguments.length我真的在这个网站上创建了一个帐户来评论Peter Ajtai的答案(目前投票率最高),结果发现你需要50个代表(不管是什么)来评论,所以我会把它作为一个答案,因为它可能值得指出几件事

他在答复中说:

您还可以传递
setTimeout
引用,因为引用不会立即执行,但是您不能传递参数:

setTimeout(playNote, delay);
setTimeout(playNote, delay);
这不是真的。在给出函数引用和延迟量后,任何附加参数都会被解析为引用函数的参数。下面的方法比在函数中包装函数调用更好

setTimeout(playNote, delay, currentaudio.id, noteTime)
始终查阅文档

正如彼得指出的,如果你想改变每个<代码>播放笔记()/代码>之间的延迟,或者如果你想在每一个代码< > PrPoEnter()/<代码>之间有相同的延迟,那么递归函数将是个好主意。
还值得注意的是,如果要将for循环的
i
解析为
setTimeout()
,则需要将其封装在函数中,详细说明如下

这可能有助于理解javascript何时执行代码,以及何时等待执行某些操作:

let foo2=function foo(bar=baz()){console.log(bar);return bar()}

  • javascript执行的第一件事是函数构造函数,并创建一个函数对象。您可以使用function关键字语法或
    =>
    语法,并得到类似的()结果
  • 然后将刚刚创建的函数分配给变量
    foo2
  • 此时,没有运行任何其他函数:没有调用其他函数(既没有
    baz
    也没有
    bar
    ),没有查找值,等等。但是,已在函数内部检查语法