在setTimeout中使用JavaScript闭包

我使用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 Events和Classes。 由于某些原因,我不能使用“singleton”(如window.renderer = new Renderer();)


你的代码很好,但是Andy的回答是误导性的,因为它将作用域链与执行上下文以及扩展名调用栈混淆。

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

执行上下文与范围链不同并且与范围链分开,因为它是在函数被调用时构建的(不管是直接调用 - func();还是作为浏览器调用的结果,例如超时过期)。 执行上下文由激活对象(该函数的参数和局部变量),以范围链的引用,和的值的this

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

如果我们要改变你的onRender函数来简单调用它自己( this.onRender() ),堆栈会很快溢出。 这是因为控制永远不会离开每个连续的onRender函数,从而允许其执行上下文从调用堆栈中弹出。 相反,我们会越来越深入,每个onRender等待下一个onRender返回,只有在堆栈溢出时才会崩溃。

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

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

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

我们可以确保你没有通过测试来泄漏内存。 我让它运行50万次,没有发现任何泄漏的内存。 请注意,最大调用堆栈大小约为1,000(因浏览器而异),所以绝对不会递归。


setTimeout函数在全局范围内执行,它们不知道它们被实例化的范围上下文。 使用Javascript递归,您必须注意的是递归太深,因为每次递归调用都会创建一个新的范围上下文,并在内存中生成。 在这种情况下,setTimeout将其重置为全局范围,所以它在技术上不是递归。

编辑:这个答案是不正确的。 看评论。

链接地址: http://www.djcxy.com/p/51703.html

上一篇: Using JavaScript closures in setTimeout

下一篇: PHP function returning anonymous function with use / global