是否优化了JavaScript引擎尾部调用(TCO)?


91

我有一个用JavaScript实现的尾部递归寻路算法,想知道是否有(全部?)浏览器可能会出现堆栈溢出异常。


2
它实际上是递归算法,还是使用递归实现的迭代算法?我的理解是,TCO仅能为后者提供帮助。
nmichaels 2010年

1
我只想补充一点,TCO并不是only一种优化。支持它应该是语言规范的一部分,而不是编译器/解释器,因为使用TCO针对一个解释器/编译器编写的代码可能无法在没有TCO的解释器/编译器上工作。
霍夫曼2014年

1
您可以在下面的Kangax的ES6兼容性表中看到当前的支持并观看它在各个引擎之间的演变:kangax.github.io/compat-table/es6/…–
Roy Tinker

Answers:


47

ECMAScript 4规范最初是要添加对TCO的支持,但已被删除:

JavaScript中没有更多的尾调用了吗?

据我所知,目前还没有广泛使用的JavaScript实现自动TCO。不过,这可能对您有用:

尾叫优化

本质上,使用累加器模式可以达到相同的效果。


1
只是FYI,Rhino在“解释”模式(opt = -1)下具有自动TCO和Continuations wiki.apache.org/cocoon/RhinoWithContinuations
Mark Porter

5
(对于拖钓很抱歉)ECMAScript 6已包含TCO,在规范中称为“正确的尾部呼叫”。
2013年

@sclv:蹦床参考什么?
2013年

39
累加器模式无法实现与TCO相同的效果。它只是将递归算法转换为尾递归形式。这是拥有TCO的前提,但不能替代TCO。您仍然会使用无法优化尾调用的语言来打击堆栈。
Marcelo Cantos

“如果没有传递正确的标记
则从

26

暂时没有喜悦,但值得庆幸的是,Harmony(ECMAScript版本6)计划正确的尾声 http://wiki.ecmascript.org/doku.php?id=harmony:proper_tail_calls


1
@MarkWilbur这个问题专门针对浏览器,而不是所有现有的ECMAScript实现。
没用的代码

1
@UselessCode不,这个问题是关于“ Javascript引擎”的,所以...不仅仅是浏览器
BT

1
@BT确实有很多非浏览器JS环境,并且标题的确使用了更通用的“ Javascript引擎”,但问题的正文指定“ ...希望了解是否有(全部?)浏览器可能会获得堆栈溢出异常”。
无用的代码

我必须反驳“但标题说……”。我认为因为他提到了两者,所以问题是关于两者的。但是,如果您说它不会使答案过时,那您是对的。
BT

4
@MarkWilbur据我所知,节点使用与chrome相同的v8版本-目前不支持TCO,我对JS有一个要点,以及当前V8产生的优化汇编程序-gist.github.com/mcfedr / 832e3553964a014621d5
mcfedr 2014年

12

您遇到的每个浏览器几乎都会拒绝“太多的递归”。这是V8错误跟踪器中条目中,可能会很有趣。

如果它是简单的自我递归,那么使用显式迭代而不是希望消除尾部调用可能值得付出努力。


该错误终于被接受。这是史诗般的:“功能请求和谐”。希望这意味着他们计划将其添加到V8中的ES6支持中。
Txangel 2014年

您可以在Internet Explorer中对TCO支持进行投票:wpdev.uservoice.com/forums/257854-internet-explorer-platform/…–
Roy Tinker

12

将来,ECMAScript 6严格模式将支持尾部调用优化。检查http://www.2ality.com/2015/06/tail-call-optimization.html有关详细信息,。

检查http://kangax.github.io/compat-table/es6/以获取当前引擎支持。

目前(18-07-2019)以下引擎支持尾部调用优化:

  • Safari> = 10
  • iOS> = 10
  • Kinoma XS6
  • 杜克塔普2.3

如果“实验性JavaScript功能”标志已打开,则支持:

  • 节点6.5
  • Chrome 54 / Opera 41 compat表的当前版本不再列出


2

当前,没有JavaScript实现能够识别尾部递归。ECMAScript 6正在进行更改,正如其他人所说的,V8上有一张公开票。

在这里,您可以看到V8生成的用于尾递归函数的汇编器:

V8如何编译递归的示例

比较一下Clang在C语言中如何编译相同的函数

C编译器尾部递归示例

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


“目前尚无JS实现能识别尾递归。” 从节点6.2.0开始是不正确的,但是您必须传递一个标志
Janus Troelsen,2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.