在setTimeout中使用JavaScript闭包

Nan*_*ako 9 javascript mootools closures settimeout

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

var Renderer = new Class (
{
    Implements: Events,

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

    onRender: function()
    {
        // some rendering actions
        setTimeout(this.onRender.bind(this), 20);
    }
});
Run Code Online (Sandbox Code Playgroud)

由于闭包的无限嵌套,该代码是否存在潜在的内存泄漏?或者一切都好吗?我到目前为止唯一的解决方案是将其重写为常规

function Renderer()
{
    var onRender = function()
    {
        // rendering
        setTimeout(onRender, 20);
    };
    onRender();
};
Run Code Online (Sandbox Code Playgroud)

但我不想失去Mootools活动和课程.由于某些原因,我不能使用"singleton"(如window.renderer = new Renderer();)

jos*_*736 19

你的代码很好,但是Andy的答案是误导性的,因为它将范围链执行上下文混淆,并且通过扩展调用堆栈.

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

执行上下文范围链是不同的,因为它是在调用函数时构建的(无论是直接func();- 还是作为浏览器调用的结果,例如超时到期).执行上下文由激活对象(函数的参数和局部变量),范围链的引用和值组成this.

所述调用栈可以被认为是执行上下文的阵列.堆栈的底部是全局执行上下文.每次调用函数时,其参数和this值都存储在堆栈中的新"对象"中.

如果我们要将你的onRender函数改为简单地调用self(this.onRender()),那么堆栈会很快溢出.这是因为控制永远不会离开每个连续的onRender函数,允许其执行上下文从调用堆栈中弹出.相反,我们越来越深入地onRender等待下一个onRender返回,在一个无限循环中,只有当堆栈溢出时才会被破坏.

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

超时到期时,浏览器onRender从全局执行上下文启动调用; 调用堆栈只有两个深度.有一个新的执行上下文 - 默认情况下会继承全局范围作为其this值; 这就是为什么你必须对bind你的Renderer对象 - 但它仍然包括你第一次定义时创建的原始范围链onRender.

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

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