A common problem with the powerful javascript closures
I recently wrote about closures and how easier your javascript will be to maintain and how good it will look. Now is the time for me to be a Closure-Grinch.
Closures keep a reference to a variable, not a copy
[source:javascript]
// Create a Buy Viagra function
function buyViagra() {
var pills = 2;
// Create a closure to alert the number of pills
function alertPills() {
alert(”Number of pills : ” + pills);
}
alertPills();
// Increment the number of pills
pills += 1;
alertPills();
}
buyViagra();
[/source]
Looking at this code, it can be pretty obvious that the alert containing the number of pills will be “2″ the first time and “3″ the second time. That’s because the closures contains the reference to the variable and not a copy.
The common problem with closures and loop
That’s a more common problem. We use closures in a loop (for or while) and it always keep the last value of the increment.
[source:javascript]
// Functions that will alert a number
var alertFunctions = new Array();
for (var iNumber = 0; iNumber < 3; iNumber++) {
function alertFunction() {
// We use the closures to have access to the variable “iNumber” (from the loop)
alert(”Number is ” + iNumber);
}
alertFunctions.push(alertFunction);
}
// We loop and call each functions
for (var i = 0; i < alertFunctions.length; i++) {
alertFunctions[i](); // Call the function
}
[/source]
We would expect that three alerts pop having 0, 1, 2. But instead, it alert 3, 3, 3. This is because the iNumber variable was 3 when it got out of the loop.
How to solve the problem
[source:javascript]
// Functions that will alert a number
var alertFunctions = new Array();
for (var iNumber = 0; iNumber < 3; iNumber++) {
// Magic here!
var alertFunction = function(x) {
return function() { alert(x); };
}(iNumber);
alertFunctions.push(alertFunction);
}
// We loop and call each functions
for (var i = 0; i < alertFunctions.length; i++) {
alertFunctions[i](); // Call the function
}
[/source]
Step #1
var alertFunction = function(x) {
I create a new function that will take a parameter. The parameter will contain a copy of the variable I send to it.
Step #2
return function() { alert(x); };
I return a function that will alert the copied parameter of step #1.
Step #3
}(iNumber);
I call the newly created function (that returns a function).


[...] will be to maintain and how good it will look. Now is the time for me to be a Closure-Grinch.read more | digg [...]
no need to define alertFunction() multiple times.
function alertFunction(x) {
return function() { alert(x) }
}
for (var iNumber = 0; iNumber
If you have access to the Prototype library, the bindAsEventListener can method can help with this.
http://wiki.script.aculo.us/scriptaculous/show/Function.prototype.bindAsEventListener
alertFunctions.push(
(function() {
alert(this);
}).bindAsEventListener(iNumber)
);
Again, the use of the Function constructor also does the trick here:
alertFunction=new Function(”alert(’Number is ” + iNumber + “);”);
alertFunctions.push(alertFunction);
BK: While that would work, new Function is just a synonym for eval(), and uses of eval() are problematic at best since “complicated ’string ” + concate + “‘ nation is a bitch to ” + write + (and * debug).
For that example, it’s probably not that important either way, since there’s little security, performance, or clarity issues at all with doing it any way, but I figured it’s worth mentioning my dislike for it in general.
jimbojw: That ends up using a closure itself anyway, hidden behind a method call.
Function.prototype.bindAsEventListener = function(/* some args */) {
var that = this;
var args = arguments;
return function() { return that.call.apply(that, args); }
}
I’m not saying it won’t work, I’m just saying that Prototype isn’t beyond closures.
@jsn : I didn’t redefine alertFunction, it’s just that I have alertFunction and alertFunctions (with a “s”).
@BK : Like Aaron said, Function is a bitch. It would be a big problem if the function would be just a bit more complex…
I agree that they’re Evil to write and maintain but still the only way (I know) of creating a function in a function without closure and for an application that would need to run in a very limited memory space, you could have no choice (except maybe to create the function outside of the function)
BK, closures are everywhere! Even with Function… try that code.
var i = 10;
var f = new Function(”alert(i);”);
f();
Update : I am wrong! Look at the next comment from BK
this works if you put it global.
Now, try this:
function test(){
var i = 10;
var f = new Function(”alert(i);”);
f();
}
test();
Result: i is undefined in both FF and IE -> No Closure
Dammit! You’re right. Thanks!
function test(){
var i = 10;
var f = new Function(’alert(’ + i + ‘);’);
f();
}
test();
This works