为什么我必须使用匿名函数而不是将附加参数传递给“setTimeout”?
Why do I have to use an anonymous function instead of passing an additional argument to `setTimeout`?
我想在 1 秒后停止 requestAnimationFrame
:
rAF = requestAnimationFrame(draw);
所以我用setTimeout
.
当我将 cancelAnimationFrame(rAF)
包裹在箭头函数中时,它工作正常:
setTimeout(() => cancelAnimationFrame(rAF), 1000);
但是,当我使用 cancelAnimationFrame
作为函数本身并将 rAF
作为第三个参数传递给 setTimeout
时,它不起作用:
setTimeout(cancelAnimationFrame, 1000, rAF);
我以为我一开始不知道 setTimeout
的确切语法。但是,我认为语法没有错,因为这段代码工作正常:
setTimeout(alert, 1000, "Hello");
为什么不起作用?
不同之处在于计算 rAF
的时间。
说明
据推测,您的代码如下所示:
let rAF;
const draw = () => {
// Do some work.
rAF = requestAnimationFrame(draw);
};
rAF = requestAnimationFrame(draw);
当你这样做时:
setTimeout(cancelAnimationFrame, 1000, rAF);
您将三个值传递给 setTimeout
:一个函数、一个数字和另一个数字。
当执行此语句时 call is evaluated,这意味着首先,setTimeout
标识符被解析为函数引用。
其次,评估三个参数:cancelAnimationFrame
标识符被解析为函数引用,然后数字文字是一个数字基元,然后 rAF
标识符被解析为另一个数字基元。
然后,call is performed.
这就是 setTimeout
看到的全部内容。
在 JavaScript 中,您不能像在 C 中那样传递对数字的引用。
让我们假设 rAF
最初是 1
。
在一秒钟的过程中,rAF
重复递增并最终达到 61
左右的值。
因为你一开始就注册了setTimeout
,语句
setTimeout(cancelAnimationFrame, 1000, rAF);
等同于
setTimeout(cancelAnimationFrame, 1000, 1);
然而,声明
setTimeout(() => cancelAnimationFrame(rAF), 1000);
不等同于
setTimeout(() => cancelAnimationFrame(1), 1000);
函数体只有在被调用时才会被评估。
这意味着,JS 不会“窥视函数内部”并尝试评估变量。
该语句本质上意味着 “使用其他函数和数字 1000 作为参数调用某个函数”。
当一秒结束并且是时候取消动画帧时,setTimeout
执行其回调。
如果回调是() => cancelAnimationFrame(rAF)
,那么它会被执行,所以函数体被求值:cancelAnimationFrame(rAF)
等价于cancelAnimationFrame(61)
.
然而,在 non-working 的情况下,cancelAnimationFrame
保持不变,参数 1
(相当于 setTimeout
时的 rAF
最初是称为)保持不变。
当您已经在第 61 帧时,您无法取消第 1 帧。
当然 setTimeout(alert, 1000, "Hello");
有效,因为 "Hello"
是静态的,只计算一次,永远不会改变。
相关
下面是可以检查此行为的更一般情况:
let greeting = "Hello";
const greet = (theGreeting) => console.log(`${theGreeting}, world!`);
const boundGreeting = greet.bind(null, greeting);
greeting = "Goodbye";
boundGreeting(); // Logs "Hello, world!".
greet(greeting); // Logs "Goodbye, world!".
bind
将第二个参数 (greeting
) 作为第一个参数传递给 greet
(忽略 null
)。
这很像使用 setTimeout(greet, 0, greeting);
,只是这与超时无关,我们自己调用(绑定)greet
。
你可以传递类似引用的东西,即一个对象,如果你有一个也接受对象的函数:
const timing = {
rAF: null
},
cancelTiming = ({ rAF }) => cancelAnimationFrame(rAF),
draw = () => {
// Do some work.
timing.rAF = requestAnimationFrame(draw);
};
timing.rAF = requestAnimationFrame(draw);
setTimeout(cancelTiming, 1000, timing);
这是有效的,因为在传递 timing
时,传递的值是一个引用。
改变它,例如通过更新 rAF
属性,在该引用可见的任何地方都可见。
但这使得编程相当麻烦。
这与 Is JavaScript a pass-by-reference or pass-by-value language? 切线相关。
备选
有一种方法可以替代 setTimeout
。
当requestAnimationFrame
calls its callback function, it passes a DOMHighResTimeStamp
, similar to what performance.now
returns。
因此,您可以在 draw
函数中进行检查:
const timeoutTimestamp = performance.now() + 1000,
draw = (now) => {
// Do some work.
if(now < timeoutTimestamp){
requestAnimationFrame(draw);
}
};
requestAnimationFrame(draw);
相关:Stop requestAnimationFrame after a couple of seconds.
我想在 1 秒后停止 requestAnimationFrame
:
rAF = requestAnimationFrame(draw);
所以我用setTimeout
.
当我将 cancelAnimationFrame(rAF)
包裹在箭头函数中时,它工作正常:
setTimeout(() => cancelAnimationFrame(rAF), 1000);
但是,当我使用 cancelAnimationFrame
作为函数本身并将 rAF
作为第三个参数传递给 setTimeout
时,它不起作用:
setTimeout(cancelAnimationFrame, 1000, rAF);
我以为我一开始不知道 setTimeout
的确切语法。但是,我认为语法没有错,因为这段代码工作正常:
setTimeout(alert, 1000, "Hello");
为什么不起作用?
不同之处在于计算 rAF
的时间。
说明
据推测,您的代码如下所示:
let rAF;
const draw = () => {
// Do some work.
rAF = requestAnimationFrame(draw);
};
rAF = requestAnimationFrame(draw);
当你这样做时:
setTimeout(cancelAnimationFrame, 1000, rAF);
您将三个值传递给 setTimeout
:一个函数、一个数字和另一个数字。
当执行此语句时 call is evaluated,这意味着首先,setTimeout
标识符被解析为函数引用。
其次,评估三个参数:cancelAnimationFrame
标识符被解析为函数引用,然后数字文字是一个数字基元,然后 rAF
标识符被解析为另一个数字基元。
然后,call is performed.
这就是 setTimeout
看到的全部内容。
在 JavaScript 中,您不能像在 C 中那样传递对数字的引用。
让我们假设 rAF
最初是 1
。
在一秒钟的过程中,rAF
重复递增并最终达到 61
左右的值。
因为你一开始就注册了setTimeout
,语句
setTimeout(cancelAnimationFrame, 1000, rAF);
等同于
setTimeout(cancelAnimationFrame, 1000, 1);
然而,声明
setTimeout(() => cancelAnimationFrame(rAF), 1000);
不等同于
setTimeout(() => cancelAnimationFrame(1), 1000);
函数体只有在被调用时才会被评估。 这意味着,JS 不会“窥视函数内部”并尝试评估变量。 该语句本质上意味着 “使用其他函数和数字 1000 作为参数调用某个函数”。
当一秒结束并且是时候取消动画帧时,setTimeout
执行其回调。
如果回调是() => cancelAnimationFrame(rAF)
,那么它会被执行,所以函数体被求值:cancelAnimationFrame(rAF)
等价于cancelAnimationFrame(61)
.
然而,在 non-working 的情况下,cancelAnimationFrame
保持不变,参数 1
(相当于 setTimeout
时的 rAF
最初是称为)保持不变。
当您已经在第 61 帧时,您无法取消第 1 帧。
当然 setTimeout(alert, 1000, "Hello");
有效,因为 "Hello"
是静态的,只计算一次,永远不会改变。
相关
下面是可以检查此行为的更一般情况:
let greeting = "Hello";
const greet = (theGreeting) => console.log(`${theGreeting}, world!`);
const boundGreeting = greet.bind(null, greeting);
greeting = "Goodbye";
boundGreeting(); // Logs "Hello, world!".
greet(greeting); // Logs "Goodbye, world!".
bind
将第二个参数 (greeting
) 作为第一个参数传递给 greet
(忽略 null
)。
这很像使用 setTimeout(greet, 0, greeting);
,只是这与超时无关,我们自己调用(绑定)greet
。
你可以传递类似引用的东西,即一个对象,如果你有一个也接受对象的函数:
const timing = {
rAF: null
},
cancelTiming = ({ rAF }) => cancelAnimationFrame(rAF),
draw = () => {
// Do some work.
timing.rAF = requestAnimationFrame(draw);
};
timing.rAF = requestAnimationFrame(draw);
setTimeout(cancelTiming, 1000, timing);
这是有效的,因为在传递 timing
时,传递的值是一个引用。
改变它,例如通过更新 rAF
属性,在该引用可见的任何地方都可见。
但这使得编程相当麻烦。
这与 Is JavaScript a pass-by-reference or pass-by-value language? 切线相关。
备选
有一种方法可以替代 setTimeout
。
当requestAnimationFrame
calls its callback function, it passes a DOMHighResTimeStamp
, similar to what performance.now
returns。
因此,您可以在 draw
函数中进行检查:
const timeoutTimestamp = performance.now() + 1000,
draw = (now) => {
// Do some work.
if(now < timeoutTimestamp){
requestAnimationFrame(draw);
}
};
requestAnimationFrame(draw);
相关:Stop requestAnimationFrame after a couple of seconds.