绑定函数是否支持 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 将在调用实际函数之前删除。

callapply方法是调用函数的函数,调用时栈上有两个调用需要尾调用优化。 (将此与 - 例如 - Array.prototype.map 进行对比,它也调用其他函数,但此处 map 执行上下文保留在调用堆栈中。在 callapply 中, Call()确实在算法的尾部位置)。