JavaScript引擎是否支持尾调用优化(TCO)?

94

我有一个尾递归的路径查找算法,已经用JavaScript实现了。我想知道是否所有浏览器都有可能出现堆栈溢出异常。


2
这实际上是一个递归算法,还是使用递归实现的迭代算法?我的理解是TCO只能帮助后者。 - nmichaels
1
我只想补充一点,TCO 不仅仅是一种优化。支持 TCO 应该成为语言规范的一部分,而不是编译器/解释器的一部分,因为针对支持 TCO 的编译器/解释器编写的代码可能无法在不支持 TCO 的编译器/解释器中工作。 - Hoffmann
1
你可以在Kangax的ES6兼容性表格中查看当前支持情况,并观察其在各个引擎中的发展,网址是:http://kangax.github.io/compat-table/es6/#proper_tail_calls_(tail_call_optimisation)。 - Roy Tinker
nmichaels:TCO(尾调用优化)是关于任何尾调用的,而不仅仅是递归尾调用。 - Maëlan
6个回答

48

ECMAScript 4 规范最初计划添加对TCO的支持,但后来被放弃了:

JavaScript中不再有尾调用?

截至2010年,目前没有广泛可用的 JavaScript 实现自动 TCO。


请查看评论以获取更近期的更新。


1
只是提醒一下,Rhino在“解释”模式(opt = -1)中具有自动TCO和Continuations功能。http://wiki.apache.org/cocoon/RhinoWithContinuations - Mark Porter
5
ECMAScript 6 已经包含了 TCO,也被称为 Proper Tail Calls 规范。 - frosty
@sclv:什么是“蹦床”引用? - bukzor
41
累加器模式无法达到尾递归优化的效果,它只是将递归算法转换为尾递归形式。这是实现尾递归优化的前提条件,但不代替尾递归优化。在不支持尾调用优化的语言中,仍然会发生堆栈溢出。 - Marcelo Cantos
尾调用优化从ES6开始得到支持。 - Elzohary
显示剩余2条评论

26

1
@MarkWilbur 这个问题特别涉及到浏览器,而不是所有现有的ECMAScript实现。 - Useless Code
1
@UselessCode 不,这个问题是关于“JavaScript引擎”的,所以不仅仅涉及浏览器。 - B T
1
@BT 确实有许多非浏览器JS环境,而标题使用了更通用的“Javascript引擎”,但问题的正文指定了“...想知道是否任何(所有?)浏览器可能会出现堆栈溢出异常。” - Useless Code
我必须反驳“但标题说……”的观点。我认为因为他提到了两者,所以问题涉及两者。但如果你说答案并不过时,那么你是对的。 - B T
4
据我所知,Node使用与Chrome相同版本的V8引擎,目前该引擎不支持尾调用优化(TCO)。我有一个包含JS代码和当前V8生成的优化汇编代码的Gist - https://gist.github.com/mcfedr/832e3553964a014621d5 - mcfedr
@MarkWilbur 一个简单的测试表明,在Node v0.12.7中,一个无限递归函数会得到“RangeError: Maximum call stack size exceeded”的错误。尝试运行node -e 'function x() { x() }; x()' - Brian McCutchon

13
未来,ECMAScript 6严格模式将支持尾调用优化。有关详细信息,请参见http://www.2ality.com/2015/06/tail-call-optimization.html
请检查http://kangax.github.io/compat-table/es6/以了解当前引擎的支持情况。
截至目前(2019年7月18日),以下引擎支持尾调用优化:
  • Safari >= 10
  • iOS >= 10
  • Kinoma XS6
  • Duktape 2.3
如果打开“experimental JavaScript features”标志,则支持以下内容:
  • Node 6.5
  • Chrome 54 / Opera 41 当前版本的兼容性表不再列出它

12
几乎每个浏览器都会在“递归过深”时出错。这里是V8 bug tracker中一个有趣的阅读条目。(链接) 如果只是简单的自递归,最好使用显式迭代而不是指望尾调用优化来解决问题。

这个 bug 终于被确认了。它属于“功能请求 Harmony”史诗。希望这意味着他们计划将其添加到 V8 的 ES6 支持中。 - Txangel
您可以在此处为Internet Explorer的TCO支持投票:https://wpdev.uservoice.com/forums/257854-internet-explorer-platform/suggestions/6850816-es6-tail-call-optimization - Roy Tinker

3
现在可以在编译为JavaScript的LispyScript中使用尾调用优化。您可以在这里阅读更多相关信息。

互相递归怎么样? - cat

2

目前没有JavaScript实现支持尾递归。在ECMAScript 6中正在进行更改,正如其他人所说,V8上有一个未解决的问题。

在这里,您可以看到V8针对尾递归函数生成的汇编程序:

V8编译递归示例

Clang编译C语言中相同函数的方式进行比较

C编译器尾递归示例

V8保留了递归调用,而C编译器已经识别出尾递归并将其转换为循环。


目前没有JS实现能够识别尾递归。但是从Node 6.2.0开始,这是不正确的,但你需要传递一个标志。 - Janus Troelsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接