JavaScript中的递归闭包

moj*_*uba 36 javascript closures

假设我有类似的东西

function animate(param)
{
    // ...
    if (param < 10)
        setTimeout(function () { animate(param + 1) }, 100);
}

animate(0);
Run Code Online (Sandbox Code Playgroud)

这是否意味着函数的本地数据的每个实例都将保存在内存中,直到有效完成,即直到param达到10?

如果实例保存在内存中,有没有更好的方法呢?我知道,传递文本代码来setTimeout()解决问题,但在我的情况下,函数参数中的对象不能轻易地表示为字符串.

Wla*_*ant 13

不,最多两个函数的本地数据实例将在任何给定的时间点保存在内存中.以下是事件的顺序:

  1. animate(0) 叫做.
  2. param == 0创建了一个闭包,它现在可以防止释放此变量.
  3. 超时火灾animate(1)被称为.
  4. param == 1创建了新的闭包,它现在可以防止释放此变量.
  5. 第一个闭包完成执行,此时它不再被引用并且可以被释放.第一次animate()调用的局部变量现在也可以释放.
  6. 现在重复从步骤3开始animate(2).

  • @jAndy:不,不是.此时可以释放内存 - 但这并不意味着内存,确切的时间仍然取决于垃圾收集器和内存分配器算法. (2认同)

jAn*_*ndy 6

实际上,你没有在那里创建递归函数.通过调用setTimeout它不再调用自己.这里创建的唯一闭包是匿名函数setTimeout,一旦执行,垃圾收集器就会识别出对前一个实例的引用可以被清除.这可能不会立即发生,但你stack overflow肯定无法创建使用它.让我们看看这个例子:

function myFunc() {
   var bigarray = new Array(10000).join('foobar');

   setTimeout(myFunc, 200);
}
myFunc();
Run Code Online (Sandbox Code Playgroud)

现在就从浏览器中查看内存使用情况.它会不断增长,但过了一段时间(对我来说是20-40秒)它会再次彻底清洁.另一方面,如果我们创建一个真正的递归,如下所示:

function myFunc() {
   var bigarray = new Array(10000).join('foobar');

   myFunc();
}
myFunc();
Run Code Online (Sandbox Code Playgroud)

我们的内存使用量将增长,浏览器会锁定,我们最终会创建它stack overflow.Javascript 没有实现Tail递归,因此我们最终会在所有情况下都出现溢出.


更新

在我的第一个例子中看起来我错了.只有在没有调用函数上下文的情况下(例如,使用匿名函数),该行为才有效.如果我们重写那样的话

function myFunc() {
   var bigarray = new Array(10000).join('foobar');

   setTimeout(function() {
     myFunc();
   }, 200);
}
myFunc();
Run Code Online (Sandbox Code Playgroud)

浏览器内存似乎不再被释放.永远长大.这可能是因为任何内部关闭都会引用bigarray.但无论如何.