A closure takes place when a function creates an environment that binds local variables to it in such a way that they are kept alive after the function has returned. A closure is a special kind of object that combines two things: a function, and any local variables that were in-scope at the time that the closure was created. In the following example, the getNameFunction() forms a closure that incorporates both the getName() function and the " Rob " string that is local to the outer function's scope. When the code is executed, it displays an alert box that says " Hello Rob! ". A second alert shows that the displayName variable now holds the getName() function, without the name variable. Finally, a third alert is brought up via a setTimeout() function and confirms that the name is still in memory, long after the initial call to getNameFunction() :
function getNameFunction(){
var name = "Rob";
return function getName() { return name; }
}
var displayName = function(){
var getName = getNameFunction();
alert( "Hello " + getName() + "!" );
return getName;
}(); //holds the getName() function
alert(displayName);
//call it again later...
setTimeout( 'alert( "Your name is " + displayName() )', 10 );
Once getNameFunction() has finished executing, it is reasonable to expect that the name variable will have been deallocated. Since the name variable is still available to the getName() function, this is obviously not the case! The key to the name variable's persistence is that getNameFunction() has become a closure. The basic closure pattern can be put to good use by implementing two components, as follows:
- A Function Factory:
Contains one or more local variables which are referenced by a local, inner function.
Returns the function and not the results of the function.
- A Calling Function: The Factory is called and its result (a function) is stored to a variable.
On subsequent calls, the calling function will hold a reference to the local variables, as they were when the closure was first created.
Here is a way to use a closure to override the document.createElement() function, so that we can add two additional parameters. Typically, we would have to store the original function so that we could use it in our own function:
// store the original
document.createElementvar _createElement = document.createElement; //override document.createElementdocument.createElement = function(tag){
var _elem = _createElement(tag);
eval("_elem." + name + " = fn");
return _elem;
}
Although there is nothing wrong with this approach, the creation of a global variable is awkward and perhaps even error-prone. A better way is to pass the original document.createElement() function to ours so that it will be retained in memory. That allows us to delegate the node creation to it. Rather than create a function that would only be called once, we can use an inline one to execute it immediately, and store the results in our overridden document.createElement() function:
document.createElement = (function (fn){
return function (type, id, className) {
var elem = fn.call(document, type);
if (id) elem.id = id;
if (className) elem.className = className; return elem;
};
})(document.createElement);
Emulating Static Variables
Variables that retain their value between function calls sound a lot like static variables found in languages like Visual Basic. Static variables can be quite useful because they offer the security of being local variables while maintaining persistence of global ones. Closures can be used to mimic static variables because changes in the enclosed variables' values are retained between calls, as the following example demonstrates:
var increment = (function(){
var myStaticVariable = 0;
return function() { return myStaticVariable++; }
})();
alert( increment() );
alert( increment() );
alert( increment() );
The above code is a variation of the basic pattern presented above in that the factory is called as an inline function to set a variable to the function of the same name ( increment ).
A limitation of the basic closure pattern is that it doesn't allow much flexibility in how the local variables are used. Moreover, since the enclosed function is a private member of the outer function, it can only be used in a sort of "black box" kind of way. For instance, the previous example was used to increment a variable from zero. If you wanted to start at, say one, instead, or wanted to decrement, you were out of luck. The next example provides more flexibility by accepting both a function and a variable to work with. That way we can pass in our own incrementor function, static variable, and initial value:
function createStaticVariable(fc, varName, initialValue){
eval("var " + varName + " = " + initialValue || "");
eval("fc = " + fc.toString());
return fc;
}
var customIncrementer = createStaticVariable( function (arg0) {
alert('window.staticVar='+window.staticVar);
alert('arg0='+arg0);
return staticVar += 2;
}, "staticVar",-1);
alert( customIncrementer() );
alert( customIncrementer('foo') );
alert( customIncrementer('bar') );
The trick here is that the staticVar variable is not defined until the createStaticVariable() function is called. Therefore, we have to get the JavaScript interpreter to evaluate the function code within the outer function in order to create the proper lexical scoping environment. This is accomplished with the help of the eval() function.
We can make the createStaticVariable() function available to all functions by adding it to the Function object's prototype . That way, we can call it without the first argument:
Function.prototype.createStaticVariable = function(varName, initialValue){
eval("var " + varName + " = " + initialValue);
eval("var fc = " + this.toString());
return fc;
}
var customIncrementer = function(arg0){
alert('window.staticVar='+window.staticVar);
alert('arg0='+arg0);
return staticVar += 2;
}.createStaticVariable("staticVar", -1);
alert( customIncrementer() );
alert( customIncrementer('foo') );
alert( customIncrementer('bar') );
Notice that the fc variable requires the var keyword in order to make it local. Without it, the fc variable would be appended to the global window object. The customIncrementer() function specifically checks the window object for the staticVar , which should be undefined .
Using Closures in Loops
Functions called from within a loop can highlight variable discrepancies between creation and execution time when global variables are referred to within dynamically created functions. The function below stores a number of functions in an array to be called later. The outer incrementor1 variable is accessed from within the inner anonymous function, so one would expect that the variable's value would be preserved until we called the function. In fact, the function shows that incrementor1 is three (3), that is the value that it is currently at after exiting the loop:
var noOfFunctions= 3;
var aFunctions = new Array();
var incrementor1 = 0;
var incrementor2 = 0;
for (; incrementor1 < noOfFunctions; incrementor1++){
aFunctions.push(function() {alert('Number=' + incrementor1);});
} //call each function
for (; incrementor2 < aFunctions.length; incrementor2++){
aFunctions[incrementor2]();
}
What we need to do is bind the incrementor1 's value to the function when we add it to the aFunctions array. Using the pattern that we learned earlier, we can create a closure around the incrementor1 variable by placing within an outer function that returns the anonymous function. Calling it as an inline function allows us to pass the incrementor1 variable to the inner function:
var noOfFunctions = 3;
var aFunctions = new Array();
var incrementor1 = 0;
var incrementor2 = 0;
for (; incrementor1 < noOfFunctions; incrementor1++){
aFunctions.push( (function setIncrementor(inc) {
return function() {
alert('Number=' + inc);
};
})(incrementor1) );
}
//call each function
for (; incrementor2 < aFunctions.length; incrementor2++){
aFunctions[incrementor2]();
}
Now, when we execute the code, it behaves as we expect and displays 1, 2, and 3 in the alert boxes.
Many JS Frameworks make extensive use of closures within functions such as bind() and curry() in save the state of variables and object with functions and events that will be using them at a later time; functions like setTimeout() and setInterval() , as well as the XMLHttpRequest 's onreadystatechange event handler. By using those, you don't even have to know the nuts and bolts of closures to take advantage of them. However, I believe that closures are worth the little bit of trouble it takes to understand them, so I would suggest that you get better acquainted with them.
Published by Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleConsulting.com. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous... View profile
