Avoiding Closures

Problem

Closure is a procedure or a function, which body contains links to the variables, declared out of the function body but not as parameters of this function (but in the surrounding code).

Closures can be the reason of the memory leak.

Solution

The article contains most frequent cases of the closures use and the ways of their elimination:

1. Subscription to the JavaScript events

Code with closure:

var oval = document.getElementById("div");

if (oval.addEventListener)

    oval.addEventListener("onclick", function (event)

    {

     //You can work with oval from here

    }, false);

else

    oval.attachEvent("onclick", function (event)

    {

     //You can work with oval from here

    });

Often, the closure is used to get access to the dom node the event is attached to.

The way to avoid closure:

function onclick(event)

{

    this  === oval

}

function test()

{

    var oval = document.getElementById("div");

    if (oval.addEventListener)

        oval.addEventListener("onclick", onclick, false);

    else

        oval.onclick = onclick;

}

The specific is that the attachEvent function is no longer used, and the onclick property/event is used instead. It is required for this context in the event handler to be equal to the dom element with the attached event.

2. The use of the setTimeout method

Code with closure:

var oval = document.getElementById("div");

setTimeout(function()

{

    oval.innerHTML = "ok";

}, 100);

The way to avoid closure:

function test(oval)

{

    oval.innerHTML = "ok";

}

setTimeout(test, 100, oval);

However, this code is not functional in the IE, because the test function in IE is always called without parameters. That is why the following function is used instead of the built-in one:

PP = {};

PP.IsIE = navigator.userAgent.indexOf(' MSIE ') > -1;

PP._onSetTimeout = function(index)

{

    var callback = PP._onSetTimeout.tims[index];

    delete PP._onSetTimeout.tims[index];

    return callback.Handler.call(this, callback.Params);

};

PP._onSetTimeout.tims = [];

PP.setTimeout = function (func, delay, params)

{

    if (PP.IsIE && typeof(params) != 'undefined')

    {

        var i = PP._onSetTimeout.tims.push({ Handler : func, Params : params}) - 1;

        return setTimeout("PP._onSetTimeout(" + i+ ")", delay);

    }

    else return setTimeout(func, delay, params);

};

3. Ajax

Code with closure:

var XHR = window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject("Microsoft.XMLHTTP");

XHR.onreadystatechange = function()

{

    if (XHR.status == 200)

        alert(XHR.responseText);   //Here it is comfortable to use link to the XHR and call the required callbacks

};

The way to avoid closure:

function onreadystatechange()

{

    if (this.status == 200)       //this – is always XMLHttpRequest

        alert(this.responseText);

};

var XHR = window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject("Microsoft.XMLHTTP");

XHR.onreadystatechange = onreadystatechange;

See also:

Web Applications Developers Knowledge Base