JavaScript Closures
===================
If you haven’t heard of JavaScript Closures, then you’re in for some good news and some good news and some bad news. Roughly speaking, closures describe how JavaScript variables are inherited in nested functions.
The good news about closures is that JavaScript will automatically retain a variable and its value as long as it’s needed, even if the original function has long disappeared. The bad news is that it may not be the variable of value that you had in mind.
Variable Scope
--------------
Variable scope is the section of code for which a variable is defined and retains its value. You can think of scope as the variable’s context. A more detailed discussion of variable scope will appear elsewhere, but the short version is new variables are created either in the context of a new function, or, by default in the global context.
It is generally received wisdom that global variables are to be avoided, or, at least minimised. In JavaScript, it is all to easy to create a new global variable, either by design or my accident. For this reason, it is always a good idea to declare new variables with the var keyword, and to use strict mode to enforce this.
JavaScript Closures
-------------------
In JavaScript, there are essentially two types of scope: Global and Function. Since functions can be nested, you can have function scopes within function scopes.
A JavaScript Closure is a sort of mini global environment. Since JavaScript only defines a new scope inside a function, the containing function becomes a closure for this purpose. It goes a little like this:
~~~javascript
var date=new Date(); // Create a date object with the current date.
var then=new Date();
setTimeout(test,2000); // Run the test() function after 2 seconds.
function test() {
var date=new Date(); // A new variable with the same name
alert(date); // Show the LOCAL date, just created
alert(then) // Show the GLOBAL then, created earlier
}
~~~
Of course, you already know that local variables created inside the fuction will take precedence over global variables with the same name; if there is no local variable, JavaScript will fall through to the global variable.
Here is another example, using a nested function:
~~~javascript
// … as before …
function test() {
var date=new Date();
doit();
function doit() {
alert(date); // Show the LOCAL date, just created
alert(then) // Show the GLOBAL then, created earlier
}
}
~~~
The doit function is nested inside the test function. In this case, neither variable is local, so the function will use the values it inherits from its parent scope.
Now, let’s make things worse:
~~~javascript
// No Global date variable now
var then=new Date();
setTimeout(test,2000); // Run the test() function after 2 seconds.
function test() {
var date=new Date();
setTimeout(doit,2000); // call doit after 2 seconds
function doit() {
alert(date); // Show the LOCAL date, just created
alert(then) // Show the GLOBAL then, created earlier
}
// return
}
~~~
This does the same job as before, but the actual messages are delayed by a further 2 seconds. The comment at the end of the function is simply there to remind you that the function will finish at this point, having put the doit function on the queue for later.
The important thing to note is that by the time the queued doit function is called, you would have expected the date variable to have long gone, together with the function in which it was defined; and in this example we have not defined a global date variable either. The good news, depending on what you were hoping for, is that the date variable inside the test function is retained until it is no longer needed.
A closure, then, is the persistent variable scope which is retained, even when the containing function has finished. In this case, the variables inside the test function are retained for the doit function.
Postponing a function isn’t the only way to run a function when the creating context is gone. You will also see the same situation when assigning an event handler. For example:
~~~javascript
function init() {
var date = new Date();
document.getElementByID('doit').onclick=function() {
alert(date);
};
}
~~~
The init function, possibly called when the window loads, defines a local variable to be displayed when you click on an element. Again, the variable and its value are retained long after the init function has finished.
All this is useful, but it comes, of course, with a trap.
Backfiring Closures
-------------------
The fact that the variable itself persists is easily misunderstood when using the data in the nested function. Take the following simple example:
~~~javascript
function test() {
var thing=3;
setTimeout(doit,2000);
function doit() {
alert(thing);
}
thing=4;
}
~~~
What is the value of thing when the function is called? Note that the alert function has as its parameter the variable thing, not a particular value. Note also that thing is not local to doit, but is inherited by it. As a result, when the time comes to display it, it will be the current value, now 4, which will be used. That is, thing is very much a live variable, and is subject to change after the event.
The Infamous Loop Problem
-------------------------
The problem is clearly demonstrated in a very common operation: assigning event handlers to multiple elements. Suppose, for example, you want to assign event handlers to a number of radio buttons inside a form.
###HTML
~~~html
~~~
Here the form has an id of contact, and the buttons share a name of choice. We attempt to assign event handlers as follows:
###JavaScript
~~~javascript
var form = document.getElementById('contact'); // Get the Form Element
var buttons = form.getElementsByName('choice'); // A collection of elements named “choice”
for(var i=0;i