为什么我必须使用匿名函数而不是向“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");
为什么它不起作用?
I want to stop this requestAnimationFrame
after 1 second:
rAF = requestAnimationFrame(draw);
So I use setTimeout
.
When I wrap the cancelAnimationFrame(rAF)
in an arrow function, it works fine:
setTimeout(() => cancelAnimationFrame(rAF), 1000);
But, when I use cancelAnimationFrame
as the function itself and pass rAF
as the third argument to setTimeout
, it doesn’t work:
setTimeout(cancelAnimationFrame, 1000, rAF);
I thought that I didn’t know the exact syntax for setTimeout
at first. But, I think the syntax isn’t wrong as this code works fine:
setTimeout(alert, 1000, "Hello");
Why doesn’t it work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
区别在于评估
rAF
的时间点。说明
大概,您的代码如下所示:
当您执行此操作时:
您将三个值传递给
setTimeout
:一个函数、一个数字和另一个数字。执行此语句时,将评估调用,这意味着首先,
setTimeout
标识符被解析为函数引用。其次,评估三个参数:
cancelAnimationFrame
标识符解析为函数引用,然后数字文字是数字基元,然后rAF
标识符解析为另一个数字原始。然后,执行调用。
这就是
setTimeout
看到的全部内容。在 JavaScript 中,您无法像在 C 中那样传递对数字的引用。
我们假设
rAF
最初为1
。在一秒钟的时间内,
rAF
不断递增,最终达到61
左右的值。由于您在开始时注册了
setTimeout
,因此该语句等效于
但是,该语句
不等效于
函数体仅在调用时才进行计算。
这意味着,JS 不会“查看函数内部”并尝试计算变量。
该语句本质上意味着“用其他函数和数字 1000 作为参数调用某个函数”。
当一秒结束并且需要取消动画帧时,
setTimeout
执行其回调。如果回调是
() =>; cancelAnimationFrame(rAF)
,然后执行它,因此函数体被求值:cancelAnimationFrame(rAF)
相当于cancelAnimationFrame(61)
。然而,在非工作情况下,
cancelAnimationFrame
保持不变,参数1
(相当于setTimeout<时的
rAF
) /code> 最初被称为)保持不变。当您已经处于第 61 帧时,您无法取消第 1 帧。
并且
setTimeout(alert, 1000, "Hello");
当然可以,因为"Hello"
是静态的,仅评估一次,永不改变。相关
这是可以检查此行为的更一般情况:
绑定
将第二个参数 (greeting
) 作为第一个参数传递给greet
(忽略null
)。这很像使用 setTimeout(greet, 0,greeting);,只不过这与超时无关,并且我们自己调用(绑定)
greet
。如果您有一个也接受对象的函数,您可以传递类似引用的东西,即对象,以使其工作:
这是有效的,因为当传递
timing
时,传递的值是一个引用。改变它,例如通过更新
rAF
属性,在该引用可见的任何地方都是可见的。但这使得编程变得相当麻烦。
这与 JavaScript 是按引用传递还是按值传递语言? 无关。
替代方案
还有一个使用
setTimeout
的替代方案。当
requestAnimationFrame
调用时它的回调函数,它传递一个DOMHighResTimeStamp
,类似于performance.now
返回。因此,您可以在
draw
函数中进行检查:相关:几秒钟后停止 requestAnimationFrame。
The difference is the moment in time at which
rAF
is evaluated.Explanation
Presumably, your code looks something like this:
When you do this:
you pass three values to
setTimeout
: a function, a number, and another number.When this statement is executed the call is evaluated, meaning that first, the
setTimeout
identifier is resolved to a function reference.Second, the three arguments are evaluated: the
cancelAnimationFrame
identifier is resolved to a function reference, then the number literal is a number primitive, then therAF
identifier is resolved to another number primitive.Then, the call is performed.
That’s all that
setTimeout
sees.In JavaScript, you cannot pass a reference to a number, like you can in C, for example.
Let’s assume
rAF
is initially1
.Over the course of one second,
rAF
has been repeatedly incremented and eventually reaches the value61
or so.Since you register the
setTimeout
at the start, the statementis equivalent to
However, the statement
is not equivalent to
The function bodies are only evaluated when they are called.
This means, JS doesn’t “peek inside the functions” and attempt to evaluate variables.
That statement essentially means “call some function with some other function and the number 1000 as arguments”.
When the one second is over and it’s time to cancel the animation frame,
setTimeout
executes its callback.If the callback is
() => cancelAnimationFrame(rAF)
, then it’s executed, so the function body is evaluated:cancelAnimationFrame(rAF)
is equivalent tocancelAnimationFrame(61)
.However, in the non-working case,
cancelAnimationFrame
stays the same, the argument1
(equivalent torAF
at the timesetTimeout
was originally called) stays the same.You can’t cancel frame 1 when you’re already at frame 61.
And
setTimeout(alert, 1000, "Hello");
works, of course, because"Hello"
is static, is only evaluated once, never changes.Related
Here’s a more general situation where this behavior can be examined:
bind
passes the 2nd parameter (greeting
) as the 1st argument togreet
(ignore thenull
).This is much like using
setTimeout(greet, 0, greeting);
, except this is unrelated to timeouts and we call (the bound)greet
ourselves.You could pass something like a reference, i.e. an object, to make it work, if you had a function that also accepts an object:
This works because when passing
timing
, the value being passed is a reference.Mutating it, e.g. by updating the
rAF
property, is visible everywhere where this reference is visible.But this makes programming quite cumbersome.
This is tangentially related to Is JavaScript a pass-by-reference or pass-by-value language?.
Alternative
There’s an alternative to using
setTimeout
.When
requestAnimationFrame
calls its callback function, it passes aDOMHighResTimeStamp
, similar to whatperformance.now
returns.So you could make the check in your
draw
function:Related: Stop requestAnimationFrame after a couple of seconds.