绑定函数是否支持 ES6 中的正确尾调用?
Do bound functions support proper tail calls in ES6?
在 ECMAScript 2015 语言规范中,Function.prototype.apply
and Function.prototype.call
的定义都包含 "Perform PrepareForTailCall()" 作为它们的步骤之一,因此我们知道这些函数支持适当的尾调用(即尾调用优化) .
然而,[[Call]] on bound function objects 的定义省略了 PrepareForTailCall()。这是否意味着绑定函数不支持正确的尾调用,并且递归调用自身的绑定函数可能会炸毁堆栈?
参见§12.3.4.1 (Runtime Semantics: Evaluation)。首先,我们做 IsInTailPosition
,然后我们做 EvaluateDirectCall
—— 结果是 F.[[Call]]
。换句话说,当我们进行实际调用时,我们已经知道我们是否处于尾部位置。
但是,正如您所说,JS 是 not tail-optimized on any browser,Safari 除外。
The definition of [[Call]]
on bound function objects omits PrepareForTailCall()
. Does this mean that bound functions do not support proper tail calls, and that a bound function calling itself recursively could blow up the stack?
没有。 PrepareForTailCall
发生在 EvaluateDirectCall
during the evaluation of the call expression, where it checks whether that expression is in a tail position. When the tail call is prepared, the current running execution context is dropped, before the function is called, dispatching on the respective [[Call]]
internal method. The new running execution context is set up in PrepareForOrdinaryCall
from the [[Call]]
method of user-defined functions. The [[Call]]
method of bound functions 中只是在此之前引入了一个额外的间接级别。
In the ECMAScript 2015 Language Specification, the definitions of Function.prototype.apply
and Function.prototype.call
both include "Perform PrepareForTailCall()
" as one of their steps, so we know that these functions support proper tail calls.
是的,这是必要的,因为 [[Call]]
method of built-in functions 设置了一个新的 运行 执行上下文(对于 "implementation-defined steps")。正是这个 "builtin context" PrepareForTailCall
将在调用实际函数之前删除。
call
和apply
方法是调用函数的函数,调用时栈上有两个调用需要尾调用优化。 (将此与 - 例如 - Array.prototype.map
进行对比,它也调用其他函数,但此处 map
执行上下文保留在调用堆栈中。在 call
和 apply
中, Call()
确实在算法的尾部位置)。
在 ECMAScript 2015 语言规范中,Function.prototype.apply
and Function.prototype.call
的定义都包含 "Perform PrepareForTailCall()" 作为它们的步骤之一,因此我们知道这些函数支持适当的尾调用(即尾调用优化) .
然而,[[Call]] on bound function objects 的定义省略了 PrepareForTailCall()。这是否意味着绑定函数不支持正确的尾调用,并且递归调用自身的绑定函数可能会炸毁堆栈?
参见§12.3.4.1 (Runtime Semantics: Evaluation)。首先,我们做 IsInTailPosition
,然后我们做 EvaluateDirectCall
—— 结果是 F.[[Call]]
。换句话说,当我们进行实际调用时,我们已经知道我们是否处于尾部位置。
但是,正如您所说,JS 是 not tail-optimized on any browser,Safari 除外。
The definition of
[[Call]]
on bound function objects omitsPrepareForTailCall()
. Does this mean that bound functions do not support proper tail calls, and that a bound function calling itself recursively could blow up the stack?
没有。 PrepareForTailCall
发生在 EvaluateDirectCall
during the evaluation of the call expression, where it checks whether that expression is in a tail position. When the tail call is prepared, the current running execution context is dropped, before the function is called, dispatching on the respective [[Call]]
internal method. The new running execution context is set up in PrepareForOrdinaryCall
from the [[Call]]
method of user-defined functions. The [[Call]]
method of bound functions 中只是在此之前引入了一个额外的间接级别。
In the ECMAScript 2015 Language Specification, the definitions of
Function.prototype.apply
andFunction.prototype.call
both include "PerformPrepareForTailCall()
" as one of their steps, so we know that these functions support proper tail calls.
是的,这是必要的,因为 [[Call]]
method of built-in functions 设置了一个新的 运行 执行上下文(对于 "implementation-defined steps")。正是这个 "builtin context" PrepareForTailCall
将在调用实际函数之前删除。
call
和apply
方法是调用函数的函数,调用时栈上有两个调用需要尾调用优化。 (将此与 - 例如 - Array.prototype.map
进行对比,它也调用其他函数,但此处 map
执行上下文保留在调用堆栈中。在 call
和 apply
中, Call()
确实在算法的尾部位置)。