Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do double closures break circular references?

I was reading about how circular references cause memory leaks in IE, but I was pretty confused on an example using a closure within closure to break the circular reference:

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    };
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

My head got all wrangled with what referenced to what, which are the closures, which are the scope objects. Can someone break it down more explicitly than MDN? Thanks.

like image 293
Tri Noensie Avatar asked Feb 21 '14 20:02

Tri Noensie


People also ask

What is a closure How might we use closure to our advantage?

Here are some advantages of closures: They allow you to attach variables to an execution context. Variables in closures can help you maintain a state that you can use later. They provide data encapsulation. They help remove redundant code.

What is circular reference in JavaScript?

A circular reference occurs if two separate objects pass references to each other. In older browsers circular references were a cause of memory leaks. With improvements in Garbage collection algorithms, which can now handle cycles and cyclic dependencies fine, this is no longer an issue.

What is circular object object in JavaScript?

Circular reference is a series of references where an object references itself directly or indirectly through a series of objects, resulting in a closed loop, appearing in most computer programming including JavaScript.


2 Answers

If you had

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
        // can access `el` here
    };
    var el = document.getElementById('el');
    el.onclick = clickHandler;
}

then el has a reference to clickHandler, but clickHandler also has a reference to el, because it's a closure. -> circular reference (in IE)

By introducing a new scope, you make el local, so it it is not accessible by clickHandler, -> no circular reference.

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
        // cannot access `el` here
    };
    (function() {
        // `el` is local to this immediately invoked function
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

So, the solution to the memory leak problem is not to introduce yet another closure, it is to create a new scope, that "shields" the values from each other (in one direction at least).

like image 140
Felix Kling Avatar answered Sep 28 '22 18:09

Felix Kling


The rules of thumb that I go by:

For JavaScript closures to be created, there has to be nested functions and the inner function must have access to or is refered to by (let's just say "touch" for short) a variable in the outer function. If either of these 2 criteria is not found, then there's no closure in JavaScript.

Memory leaks occur when such variable (mentioned in the above paragraph) in the outer function happens to be a DOM element.

In the Mozilla article, let's visit the first example:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    };
}

Clearly, there is one function nested inside another function, and the inner function is assigned to a property of a variable (el) in the outer scope, so there is a closure created. This variable el also happens to be a DOM element, hence there's memory leak as explained in the article.

In the second example that you posted,

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    };
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

There's an outer function, and nested inside this outer function are 2 nested (inner) functions, but they are at the same level, meaning one is not nested inside the other. The second nested function also has access to a local variable (clickHandler) in the outer function, so there's a closure that gets created. However, there's no memory leaks because this local variable (clickHandler) in the outer function is not a DOM element. The local variable el does not contribute to memory leaks because it's local to the second nested function and not defined in the outer function addHandler(). In other words, it is local to the second nested function, it is not accessible to the first nested function, hence there's no chance for memory leaks.

like image 34
Kevin Le - Khnle Avatar answered Sep 28 '22 16:09

Kevin Le - Khnle