【发布时间】:2015-05-01 02:02:39
【问题描述】:
我在 JS 方面不是很有经验。但与大多数人一样,我有时需要为浏览器添加一些额外的功能。
在寻找其他问题的答案时,我遇到了this answer at SO. 答案和the responder 得到了高度评价,按照 SO 标准,这意味着它们非常可靠。 引起我注意的是,在“整理”的变体中,它使用尾递归来循环函数:
(function myLoop (i) {
setTimeout(function () {
alert('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10);
在我看来,这看起来像是糟糕的工程。在命令式/OO语言中使用递归解决非递归问题是自找麻烦。 10 或 100 次迭代应该是安全的。但是 10000 或无限循环呢? 在 Erlang 和 Haskell 等纯函数式语言中,我知道尾递归在编译期间会转换为循环,并且不会向堆栈添加额外的帧。据我所知,并非所有编译器都如此,例如 C/C++ 或 Java。
JS 怎么样?在没有 SO 风险的情况下使用尾递归是否安全?还是这取决于脚本运行的实际解释器?
【问题讨论】:
-
大部分选票来自这样一个事实,即这是一个很容易通过谷歌搜索的问题,而不是它特别好。代码不好,他们应该改用
setInterval()。 -
setTimeout 用于在调用后 x 毫秒调用您的函数。每 x 毫秒执行一次是一种常见的模式。在这种情况下,for 循环需要调用所有 setTimeout,每个都有一个已知的延迟:x * 3000。不知道它是否会导致 SO,因为它不是真正的递归。
-
@Haketo 为什么称其为“不是真正的递归”?您如何对此进行分类,为什么?对我来说,这看起来就像我将如何在 Erlang 中通过尾递归实现循环。
-
@EinarSundgren a
setTimeout在“主”执行堆栈上添加一个函数调用,而不是在调用它的同一堆栈上,所以它不会在最新的堆栈上添加堆栈。 (例如,我不知道 Erlang) -
@Haketo 所以实际上是因为使用的函数的属性?如果它是任何其他的,例如建议的'setIntervall()',答案会不同吗?正如我所说,我是一个 JS 涉足者,试图磨练我的技能。
标签: javascript recursion tail-recursion