我正在使用setTimeout来模拟渲染,并且我来到了这样的结构:

var Renderer = new Class (
{
    Implements: Events,

    initialize()
    {
        this.onRender();
    },

    onRender: function()
    {
        // some rendering actions
        setTimeout(this.onRender.bind(this), 20);
    }
});

由于闭包的无限嵌套,该代码是否存在潜在的内存泄漏?还是一切正常?到目前为止,我唯一的解决方案是将其重写为平常的
function Renderer()
{
    var onRender = function()
    {
        // rendering
        setTimeout(onRender, 20);
    };
    onRender();
};

但是我不想失去Mootools事件和类。
由于某些原因,我也不能使用“单例”(例如window.renderer = new Renderer();)

最佳答案

您的代码很好,但是Andy的答案具有误导性,因为它混淆了scope chainexecution context,并扩展了调用堆栈。

首先,setTimeout函数不在全局范围内执行。它们仍然是execute in a closure,并且可以从外部范围访问变量。这是因为JavaScript使用静态范围;也就是说,函数的作用域链是在函数创建时就定义的,并且永不更改;范围链是函数的属性。

执行上下文是不同的,并且与作用域链不同,它是在调用函数时构造的(无论是直接-func();-还是浏览器调用的结果(例如超时))。执行上下文由激活对象(函数的参数和局部变量),对作用域链的引用以及this的值组成。

可以将调用堆栈视为执行上下文的数组。堆栈的底部是全局执行上下文。每次调用函数时,其参数和this值都存储在堆栈上的新“对象”中。

如果我们将onRender函数更改为仅调用自身(this.onRender()),则堆栈将很快溢出。这是因为控件永远不会离开每个连续的onRender函数,从而允许其执行上下文从调用堆栈中弹出。取而代之的是,我们在每个onRender等待下一个onRender返回的过程中越走越深,只有在堆栈溢出时才会中断的无限循环中。

但是,通过调用setTimeout,控件将立即返回,因此可以离开onRender函数,从而导致其执行上下文从堆栈中弹出并被丢弃(由GC从内存中释放出来)。

超时到期后,浏览器会从全局执行上下文中启动对onRender的调用;调用堆栈只有两个深度。有一个新的执行上下文–默认情况下,它将继承全局范围作为其this值;这就是为什么您必须bindRenderer对象的原因–但是它仍然包含最初定义onRender时创建的原始作用域链。

如您所见,您不是通过递归来创建无限闭包,因为闭包(作用域链)是在函数定义而不是在函数调用时创建的。此外,您不会创建无限执行上下文,因为在onRender返回之后它们将被丢弃。

我们可以通过testing it确保您不会泄漏内存。我让它运行了500,000次,并且没有观察到任何泄​​漏的内存。请注意,最大调用堆栈大小约为1,000(因浏览器而异),因此绝对不会递归。

关于javascript - 在setTimeout中使用JavaScript闭包,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7721200/

10-09 23:23