Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/380.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaScript中的递归闭包_Javascript_Closures - Fatal编程技术网

JavaScript中的递归闭包

JavaScript中的递归闭包,javascript,closures,Javascript,Closures,假设我有 function animate(param) { // ... if (param < 10) setTimeout(function () { animate(param + 1) }, 100); } animate(0); 函数动画(参数) { // ... 如果(参数

假设我有

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

animate(0);
函数动画(参数)
{
// ...
如果(参数<10)
setTimeout(函数(){animate(param+1)},100);
}
动画(0);
这是否意味着函数本地数据的每个实例都将保存在内存中,直到animate完成,即直到param达到10


如果实例保存在内存中是真的,那么有更好的方法吗?我知道,将文本代码传递给
setTimeout()
解决了这个问题,但在我的例子中,函数参数中有一些对象不能很容易地表示为字符串。

只有与最近调用
animate
相关的上下文才会被保留(理论上,这完全取决于垃圾收集器)。当
animate
创建匿名函数时,匿名函数将获得对调用
animate
的上下文的引用,因此该上下文将保留在内存中。当超时发生时,计时器代码对匿名函数的引用被释放,这将释放该函数对上下文的引用。同时创建了一个新上下文,但新上下文不引用旧上下文,因此不会将旧上下文保留在内存中

关键是上下文与函数的绑定发生在函数创建时,而不是调用时。

如何

function animate(param)
{
  //....
  if(param < 10)
    animateTimeout(param);
}

function animateTimeout(param)
{
   setTimout(function() { animate(param + 1) }, 100 );
}
函数动画(参数)
{
//....
如果(参数<10)
animateTimeout(参数);
}
函数animateTimeout(参数)
{
setTimout(function(){animate(param+1)},100);
}
这样您就不会将本地数据存储在//。。。在等待超时时


不确定你是否认为这里的问题比实际问题更多。您的代码不是递归的,因为它将导致10个深闭包链,因为一旦进行第二次动画调用,闭包1就会退出。每个闭包仅在一个setTimeout的生存期内存在。

是的,在您的示例中,每次执行animate函数时,都会创建一个具有自己闭包范围的新函数

为了避免出现多个闭包,您可以尝试以下方法:

function animate (param) {
    function doIt () {
        param++;
        if (param < 10) {
            setTimeout(doIt, 100);
        }
    };
    setTimeout(doIt, 100);
}
function myFunc() {
   var bigarray = new Array(10000).join('foobar');

   myFunc();
}
myFunc();
函数动画(参数){
函数doIt(){
param++;
如果(参数<10){
setTimeout(doIt,100);
}
};
setTimeout(doIt,100);
}

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

  • 调用
    动画(0)
  • 创建了
    param==0
    的闭包,它现在阻止释放此变量
  • 超时触发,调用
    动画(1)
  • 创建了
    param==1
    的新闭包,它现在阻止释放此变量
  • 第一个闭包完成执行,此时它不再被引用,可以被释放。现在还可以释放第一个
    animate()
    调用中的局部变量
  • 从步骤3开始重复,现在使用
    设置动画(2)

  • 实际上,这里没有创建递归函数。通过调用
    setTimeout
    它不再调用自己了。这里创建的唯一闭包是
    setTimeout
    的匿名函数,一旦执行该函数,垃圾收集器将识别到对前一个实例的引用可以被清除。这可能不会立即发生,但您肯定无法使用它创建
    堆栈溢出。让我们来看看这个例子:

    function myFunc() {
       var bigarray = new Array(10000).join('foobar');
    
       setTimeout(myFunc, 200);
    }
    myFunc();
    
    现在从浏览器查看内存使用情况。它会不断生长,但过一段时间(对我来说是20-40秒),它会再次被完全清洗干净。另一方面,如果我们创建这样一个真正的递归:

    function animate (param) {
        function doIt () {
            param++;
            if (param < 10) {
                setTimeout(doIt, 100);
            }
        };
        setTimeout(doIt, 100);
    }
    
    function myFunc() {
       var bigarray = new Array(10000).join('foobar');
    
       myFunc();
    }
    myFunc();
    
    我们的内存使用会增加,浏览器会锁定,我们最终会造成
    堆栈溢出。Javascript没有实现尾部递归,因此在所有情况下都会导致溢出


    更新

    看起来我在第一个例子中错了。只有在未调用函数上下文(例如使用匿名函数)时,该行为才是正确的。如果我们再这样写

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

    浏览器内存似乎不再被释放。永远成长。这可能是因为任何内部closured都保留了对
    bigarray
    的引用。但是无论如何。

    那么AnimateMout的实例将被困在内存中,不是吗?不是,一旦setTimeout中的代码被执行,第一个实例就可以被垃圾收集,因为一旦setTimeout中的代码被执行,并且在第一次调用animate时没有返回任何内容来引用局部变量。哦,对不起,在回答animateTimeout时-是-但是只进行1次迭代。我假设在您的评论部分中有更多本地数据,您希望避免存储这些数据。如果你担心的是10个深层次的问题,那么忽略这段代码,因为这根本不会发生。但在OP的原始代码中,与早期调用相关的上下文在与它们相关的计时器触发后不再被引用,因此它们可以自由地被GC调用。例如,计时器第二次启动时,第一次调用的上下文不再被任何东西引用。@T.J.Crowder,这是我对这个问题的理解,这实际上是可取的。是的,这也是我的理解。我不明白为什么它需要重铸(不是重铸的方式有什么问题。或者只是…
    setTimeout(animate,100,param+1)
    –它使用了零个额外的闭包。看起来像步骤5。一旦外部上下文中有任何变量,它的激活对象就会被清空,因此,它的激活对象被填满。@jAndy:不,不是。此时可以释放内存,但这并不意味着它会被释放,确切的时间仍然取决于垃圾收集器和内存分配算法。很好的答案,我想写一些类似的东西,但现在不需要了:-)我认为外部闭包(
    animate