我正在使用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 chain和execution context,并扩展了调用堆栈。
首先,setTimeout
函数不在全局范围内执行。它们仍然是execute in a closure,并且可以从外部范围访问变量。这是因为JavaScript使用静态范围;也就是说,函数的作用域链是在函数创建时就定义的,并且永不更改;范围链是函数的属性。
执行上下文是不同的,并且与作用域链不同,它是在调用函数时构造的(无论是直接-func();
-还是浏览器调用的结果(例如超时))。执行上下文由激活对象(函数的参数和局部变量),对作用域链的引用以及this
的值组成。
可以将调用堆栈视为执行上下文的数组。堆栈的底部是全局执行上下文。每次调用函数时,其参数和this
值都存储在堆栈上的新“对象”中。
如果我们将onRender
函数更改为仅调用自身(this.onRender()
),则堆栈将很快溢出。这是因为控件永远不会离开每个连续的onRender
函数,从而允许其执行上下文从调用堆栈中弹出。取而代之的是,我们在每个onRender
等待下一个onRender
返回的过程中越走越深,只有在堆栈溢出时才会中断的无限循环中。
但是,通过调用setTimeout
,控件将立即返回,因此可以离开onRender
函数,从而导致其执行上下文从堆栈中弹出并被丢弃(由GC从内存中释放出来)。
超时到期后,浏览器会从全局执行上下文中启动对onRender
的调用;调用堆栈只有两个深度。有一个新的执行上下文–默认情况下,它将继承全局范围作为其this
值;这就是为什么您必须bind
到Renderer
对象的原因–但是它仍然包含最初定义onRender
时创建的原始作用域链。
如您所见,您不是通过递归来创建无限闭包,因为闭包(作用域链)是在函数定义而不是在函数调用时创建的。此外,您不会创建无限执行上下文,因为在onRender
返回之后它们将被丢弃。
我们可以通过testing it确保您不会泄漏内存。我让它运行了500,000次,并且没有观察到任何泄漏的内存。请注意,最大调用堆栈大小约为1,000(因浏览器而异),因此绝对不会递归。
关于javascript - 在setTimeout中使用JavaScript闭包,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7721200/