Exploring Javascript closures

Javascript closures are a concept that is easier to explain by example:

function outer(increment) {
    var outer_foo = 1;
    function inner() {
        outer_foo += increment;
        return outer_foo; 
    }
    return inner;
}

var outer_instance = outer(2);
var foo1 = outer_instance();
var foo2 = outer_instance();  

alert(foo1);   //3
alert(foo2);   //5

The inner function references the local variables of the outer function. The inner function is the closure. It is enclosed by its parent function and has access to its parent’s execution context, even after the parent has finished execution and returned. In essence, variables in the scope chain of the parent function stay in scope, as though they were allocated on the heap and not on the stack. Contrast this to programming languages such as C, where parameters and variables local to a function are declared on the stack frame, and immediately deallocated after the function has exited.

Because the parent function’s variables stay in scope and are essentially passed by reference to the closure, this can lead to bugs. Case in point:

function createFunctions() {
    var result = new Array();

    for (var i = 0; i < 10; i++) {
        result[i] = function() { return i; };
    }

    return result;
}

var functionArray = createFunctions();
var func1 = functionArray[0];
var foo1 = func1();  
alert(foo1); //10
var func7 = functionArray[6];
var foo7 = func7(); 
alert(foo7) //10

The createFunctions method returns an array of closures, each of which is a function that returns the value of the loop index, i. At first glance, it would seem as though the closures would return the numbers 0 through 9, respectively. In actuality, every closure in the array returns 10. This is because the i variable in each closure references the i variable of the parent execution context. Because this is a reference, the value of i will not be the value at the time of the creation of the closure, but rather at the time of the execution of the closure. The for loop will have already terminated by this point and set i to 10.

In order to match the original intent behind the function, createFunctions() can be rewritten as follows:

function createFunctions2() {
    var result = new Array();

    for (var i = 0; i < 10; i++) {
        result[i] = function(num) { 
	    return function() {
	        return num; 
	    };
	}(i);
    }

    return result;
}

This syntax can be a bit jarring to read at first. The inner-most anonymous function is a closure that references the variable num, which is scoped to its parent function:

 
    //snipped out code ... 
        function() {
            return num; 
        }
    //...

The inner function is the return value for its parent function, which takes in a single argument: num. This parent function is immediately invoked with the loop-counter i as its parameter, as denoted by the “(i)” found at the end of the function declaration.

//snipped out code ...
        function(num) { 
	   //...
	}(i);
//...

Because parameters are passed in by value and not by reference, num retains the value of i at the time this outer function was created. In other words, num will equal the value of i at the current iteration in the for loop, and will keep this value independent of any future modifications to i. The outer anonymous function serves as an intermediary: It returns a closure that returns the value of num “frozen” at that point in time, allowing the code to function as originally intended.

   function createFunctions2() {
	var result = new Array();

	for (var i = 0; i < 10; i++) {
		result[i] = function(num) { 
			return function() {
				return num; 
			};
		}(i);
	}

    return result;
}

var functionArray = createFunctions2();
       
//prints out the numbers 0 through 9
for (var i = 0; i < 10; i++)
{
	var bub = functionArray[i]();
	alert(bub);
}

The key takeaway here is that closures access variables in their parent’s scope chain by reference, and not by value. To bypass this intended behavior, the parent variables that the closure accesses must themselves by a copy of the originals, which is accomplished by passing them in as parameters to the parent function. The code that accomplishes this can look unwieldy, but its not so bad once you figure out the basic pattern behind it.

Leave a Reply

Your email address will not be published. Required fields are marked *