根据v8票,它说
尾部调用消除与JavaScript不兼容,因为它在真实中使用 世界。请考虑以下事项:
function foo(x) {
return bar(x + 1);
}
function bar(x) {
return foo.arguments[0];
}
foo(1)
返回1.
它没有明确解释如果JavaScript支持尾调用,foo(1)的值是什么以及为什么?
任何人都可以解释?
答案 0 :(得分:5)
值得一点解释。对话就像这样开始(代码重新格式化):
如果我在Chrome中打开JavaScript控制台并写下:
function fac(n, a) { if (n == 0) { return a; } else { return fac(n - 1, a * n); } } fac(100000, 1);
我明白了:RangeError:超出了最大调用堆栈大小
我认为V8可能是其他编程的一个很好的目标虚拟机 语言,如果它支持尾调用。这是我能看到的唯一重大障碍 对于具有功能特征的语言。
如果您测试代码,您会注意到fac
适用于较低的值,返回Infinity
以获取较高的值并导致浏览器抛出RangeError
(最大调用堆栈大小)超过)以获得更高的价值。
原因是从另一个函数中调用的每个函数都被添加到“调用堆栈”中,这涉及一些内存开销。有了足够的递归,环境就会耗尽内存。
这可以通过“尾部调用消除”在其他语言中工作,或者不需要将调用添加到调用堆栈。例如,可以存在与正常函数不同的函数式事物,因为它们返回时会导致调用函数返回。这可以消除添加到调用堆栈的需要,这意味着递归本质上是无限的。有关深入解释,请参阅维基百科上的tail call文章。
对上述消息的响应只是提供了一个原因,即尾部调用消除(从调用堆栈中删除函数)与其他功能(Function#arguments)
不兼容,能够使用非标准功能。
答案 1 :(得分:2)
它没有清楚地解释如果JavaScript支持尾调用什么 将是foo(1)的价值和为什么?
执行1
时,该值为foo(1)
因为foo
函数返回bar
函数的结果而bar
函数除了读取第一个函数之外什么都不做带有foo
的{{1}}函数的参数(foo.arguments[0]
是每个函数可用的隐含对象,用于读取传递给函数的参数)并返回它。当你这样做时,arguments
的第一个参数恰好是foo
:
1
以下是细分:
foo(1);
function foo(x) {
return bar(x + 1); // foo calls bar function which returns 1
}
function bar(x) {
return foo.arguments[0]; // bar reads first argument passed to foo which is 1
}
foo(1); // 1 is x in foo function
函数只读取bar
的第一个参数(通过foo
)并返回它,因为没有添加。
答案 2 :(得分:1)
为了清楚起见,尾调用消除是一种节省堆栈空间的优化技术,特别适用于递归。当函数以对另一个函数的调用结束时,尾调用消除可以避免分配另一个堆栈帧来调用被调用函数。相反,它将重用(破坏和重新利用)调用函数的堆栈帧,因为它(可能)不再需要它。但是,该示例显示JavaScript代码可能仍需要调用者的堆栈帧。
它没有清楚地解释如果JavaScript支持尾调用什么 将是foo(1)的价值和为什么?
如果支持尾部调用消除,则在对foo
进行尾调用时将销毁有关bar
调用的信息,因此foo.arguments[0]
将是错误。如果给定代码起作用,则无法进行尾调用消除。