Loading

Javascript Closures

by drk last updated on 2014/09/13 ( programming / javascript )


What is it?

  • When you create a function within another function, the inner one will have access to the outer function variables (as well as its own, plus the global variables).
  • A closure is then, a reference to a function, plus the reference to non-local variables (from the outer function).
  • Even when the outer function ends (is returned), the inner function can still access the outer function variables, they aren't destroyed.
  • A closure keeps track of the references to the outer function variables, it doesn't have a copy of its value. So each of this variables can be modified at any time.

Examples

Basic example

var createClosure = function()
    {
    var count = 0;

    return function()
        {
        count++;

        return count;
        };
    };

    // we get the inner function + the variables it has access to (a closure))
var a = createClosure();

    // since its a function, means we can call it
a();    // 1
a();    // 2

    // we can create other closure, which is independent of the first one
var b = createClosure();

b();    // 1
a();    // 3
b();    // 2

Generators

function makeList( limit )
{
var list = [];

for (var a = 0 ; a < limit ; a++)
    {
    list.push( a * a );
    }

return list;
}


var limit = 10;
var list1 = makeList( limit );      // returns an array with all the elements

for (var a = 0 ; a < limit ; a++)
    {
    console.log( list1[ a ] );
    }


    // versus

function generateList( limit )
{
var a = -1;

return function()
    {
    a++;

    return a * a;
    };
}

var limit = 10;
var list2 = generateList( limit );      // returns a way to get the elements

for (var a = 0 ; a < limit ; a++)
    {
    console.log( list2() );             // get one element at a time
    }

Static variables

var generateId = (function()
    {
    var id = 0;     // keeps its value between calls

    return function()
        {
        id++;

        return id;
        };
    })();


console.log( generateId() );    // 1
console.log( generateId() );    // 2
console.log( generateId() );    // 3

Closures in loops

<ul>
    <li>one</li>
    <li>two</li>
    <li>three</li>
</ul>
function setEvents()
{
var elements = document.querySelectorAll( 'li' );

for (var a = 0 ; a < elements.length ; a++)
    {
    elements[ a ].onclick = function()
        {
            // will not work as expected, since we're saving
            // a reference to 'a' in the closure, not its value
        console.log( a + 1 );
        }
    }
}

// it will log the value '4' when you click on it,
//     because that's the current value of 'a'
// to 'fix' this, you need to pass the value of 'a'


function setEvents2()
{
var elements = document.querySelectorAll( 'li' );

    // a closure is created where a new variable ('value')
    // is created to hold the value of 'a'
var log = function( value )
    {
    return function()
        {
        console.log( value + 1 );
        };
    };

for (var a = 0 ; a < elements.length ; a++)
    {
    elements[ a ].onclick = log( a );
    }
}


    // or just simplify like this

function setEvents3()
{
var elements = document.querySelectorAll( 'li' );


for (var a = 0 ; a < elements.length ; a++)
    {
    elements[ a ].onclick = (function( value )
        {
        return function()
            {
            console.log( value + 1 );
            }
        })( a );
    }
}

Modules (private/public variables/methods)

var Module = (function()
{
var PRIVATE = 2;    // can't be accessed outside


var Public = {};

Public.variable = 1;
Public.doStuff = function( aNumber )
    {
    return PRIVATE + aNumber;
    };

    // what we return here, is what ends up
    // being accessible from outside (public)
return Public;

}());

console.log( Module.variable );     // 1
console.log( Module.doStuff( 3 ) ); // 5
console.log( Module.PRIVATE );      // undefined
back

Categories