Javascript Concepts Simplified Part 5: Javascript Closures
June 6, 2010 1 Comment
Closures are a tricky concept in Javascript. They seem deceptively simple but there are a lot of intricacies that can trip up developers. To start understanding closures, we need to start with scope – in Javascript, a function has access to it’s parent’s context, grandparent’s context, etc. up the scope chain. To see this try the code,
var X = "window"; function outermost() { var Y = "outermost"; function inner() { // Knows about the parent function's Y alert(Y); function innermost() { // Knows about the grandparent function's Y alert(Y); // Knows about the greatgrandparent's X alert(X); } innermost(); } inner(); } outermost();
This seems simple enough, even expected. Now here’s the trick, let’s return some function pointers,
var X = "window"; function outermost() { var Y = "outermost"; function inner() { // Knows about the parent function's Y alert(Y); function innermost() { // Knows about the grandparent function's Y alert(Y); // Knows about the greatgrandparent's X alert(X); } // 'inner' returns a reference to the 'innermost' function return innermost; } // 'outermost' returns a reference to the 'inner' function return inner; }
Now let’s call these functions,
// Get the reference to the 'inner' function var inr = outermost(); // Call the 'inner' function inr(); // alerts 'outermost' // Get the reference to the innermost function var inmost = inr(); // alerts 'outermost' // Call the 'innermost' function inmost(); // alerts 'outermost' and 'window'
As you can see, when we call the ‘innermost’ function, using the reference returned by the call to ‘inner’, it still has access to the X and Y values as they were in scope when the ‘innermost’ function was defined. This is Javascript’s closure – capturing information at the point of definition, so that the information can be used at the point of execution. Also the variables are not copies – they are references.
A very common problem that occurs is in the unintended creation of closure,
for (var i = 0; i < 5; i++) { // This creates a closure! elements[i].onclick = function() { alert(i); }; }
Now clicking on any element will alert ‘4’, why? Because all of the functions we created have a reference (not a copy) to the outer var i, probably not the effect you want. To fix this,
for (var i = 0; i < 5; i++) { // Don't create a function - no closure elements[i].onclick = Handle(i); } function Handle(i) { return function() { alert(i); }; }
You can read up more about closures here.