For some reason closures seem really hard to
understand when you read about them, but when you see some examples you can
click to how they work (it took me a while).
I recommend working through the examples carefully until you understand how
they work. If you start using closures without fully understanding how they
work, you would soon create some very weird bugs!
Example 3
This example shows that the local variables
are not copied - they are kept by reference. It is kind of like keeping a
stack-frame in memory when the outer function exits!
function say667() {
// Local variable that ends up within closure
var num = 666;
var sayAlert = function() { alert(num); }
num++;
return sayAlert;
}
Example 4
All three global functions have a common
reference to the same closure because they are all declared within
a single call to setupSomeGlobals().
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gAlertNumber = function() { alert(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}
The three functions have shared access to the same closure - the local
variables of setupSomeGlobals() when the three
functions were defined.
Note that in the above example, if you click
setupSomeGlobals() again, then a new closure (stack-frame!) is
created. The old gAlertNumber, gIncreaseNumber,
gSetNumber variables are overwritten with new functions that
have the new closure. (In JavaScript, whenever you declare a function inside
another function, the inside function(s) is/are recreated again each
time the outside function is called.)
Example 5
This one is a real gotcha for many people, so
you need to understand it.
Be very careful if you are defining a function within a loop: the local
variables from the closure do not act as you might first think.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
The line result.push( function() {alert(item + ' '
+ list[i])} adds a reference to an anonymous function three times to
the result array. If you are not so familiar with anonymous functions think
of it like:
Note that when you run the example, "item3 undefined" is alerted three
times! This is because just like previous examples, there is only one
closure for the local variables for buildList.
When the anonymous functions are called on the line
fnlist[j](); they all use the same single closure, and they use the
current value for i and item within
that one closure (where i has a value of
3 because the loop had completed, and
item has a value of
'item3').
Example 6
This example shows that the closure contains
any local variables that were declared inside the outer function before it
exited. Note that the variable alice is
actually declared after the anonymous function. The anonymous function is
declared first: and when that function is called it can access the
alice variable because
alice is in the closure.
Also sayAlice()(); just directly calls the
function reference returned from sayAlice() -
it is exactly the same as what was done previously, but without the temp
variable.
function sayAlice() {
var sayAlert = function() { alert(alice); }
// Local variable that ends up within closure
var alice = 'Hello Alice';
return sayAlert;
}
Tricky: note also that the sayAlert
variable is also inside the closure, and could be accessed by any other
function that might be declared within sayAlice()
or it could be accessed recursively within the inside function.
Example 7
This final example shows that each call
creates a separate closure for the local variables. There is not a
single closure per function declaration. There is a closure for each
call to a function.
function newClosure(someNum, someRef) {
// Local variables that end up within closure
var num = someNum;
var anArray = [1,2,3];
var ref = someRef;
return function(x) {
num += x;
anArray.push(num);
alert('num: ' + num +
'\nanArray ' + anArray.toString() +
'\nref.someVar ' + ref.someVar);
}
}