I'm using setTimeout to emulate rendering, and I came to the structure like this:
var Renderer = new Class (
{
Implements: Events,
initialize()
{
this.onRender();
},
onRender: function()
{
// some rendering actions
setTimeout(this.onRender.bind(this), 20);
}
});
Does that code have potential memory leaks because of infinite nesting of closures? Or everything is ok? The only solution I came so far is to rewrite it to usual
function Renderer()
{
var onRender = function()
{
// rendering
setTimeout(onRender, 20);
};
onRender();
};
But I don't want to lose Mootools Events and Classes. For some reasons I can't use a "singleton" (like window.renderer = new Renderer();) too
Your code is fine, but Andy's answer is misleading because it confuses the scope chain with execution context and, by extension, call stack.
First, setTimeout
functions do not execute in the global scope. They still execute in a closure and can access variables from outer scopes. This is because JavaScript uses static scope; that is, the scope chain of a function is defined at the moment that function is created and never changes; the scope chain is a property of the function.
Execution context is different and separate from the scope chain in that it is constructed at the time a function is invoked (whether directly – func();
– or as the result of a browser invocation, such as a timeout expiring). The execution context is composed of the activation object (the function's parameters and local variables), a reference to the scope chain, and the value of this
.
The call stack can be thought of as an array of execution contexts. At the bottom of the stack is the global execution context. Each time a function is called, its parameters and this
value are stored in a new 'object' on the stack.
If we were to change your onRender
function to simply call itself (this.onRender()
), the stack would quickly overflow. This is because control would never leave each successive onRender
function, allowing its execution context to be popped off the call stack. Instead, we go deeper and deeper with each onRender
waiting for the next onRender
to return, in an infinite cycle broken only when the stack overflows.
However, with a call to setTimeout
, control returns immediately and thus is able to leave the onRender
function, causing its execution context to be popped off the stack and discarded (to be freed from memory by the GC).
When the timeout expires, the browser initiates a call to onRender
from the global execution context; the call stack is only two deep. There is a new execution context – which by default would inherit the global scope as its this
value; that's why you have to bind
to your Renderer
object – but it still includes the original scope chain that was created when you first defined onRender
.
As you can see, you're not creating infinite closures by recursion because closures (scope chains) are created at function definition, not at function invocation. Furthermore, you're not creating infinite execution contexts because they are discarded after onRender
returns.
We can make sure you're not leaking memory by testing it. I let it run 500,000 times and didn't observe any leaking memory. Note that the maximum call stack size is around 1,000 (varies by browser), so it's definitely not recursing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With