I have read a number of explanations about closures and closures
inside loops. I have a hard time understanding the concept. I have this code: Is there a
way to reduce the code as much as possible so the concept of closure can be made
clearer. I am having a hard time understanding the part in which the
i
is inside two parenthesis.
Thanks
function addLinks ()
{
for (var i=0, link; i<5; i++) {
link =
document.createElement("a");
link.innerHTML = "Link " +
i;
link.onclick = function (num) {
return
function () {
alert(num);
};
}(i);
document.body.appendChild(link);
}
}
window.onload =
addLinks;
WARNING: Long(ish)
Answer
This is copied
directly from an article I wrote in an internal company
wiki:
Question: How to properly use
closures in loops?
Quick answer: Use a function
factory.
for (var i=0; i<10;
i++) {
document.getElementById(i).onclick = (function(x){
return
function(){
alert(x);
}
})(i);
}
or the more easily
readable version:
function
generateMyHandler (x) {
return function(){
alert(x);
}
}
for (var i=0; i<10; i++) {
document.getElementById(i).onclick = generateMyHandler(i);
}
This often confuse
people who are new to javascript or functional programming. It is a result of
misunderstanding what closures are.
A closure
does not merely pass the value of a variable or even a reference to the variable. A
closure captures the variable itself! The following bit of code illustrates
this:
var message =
'Hello!';
document.getElementById('foo').onclick =
function(){alert(message)};
message =
'Goodbye!';
Clicking
the element 'foo' will generate an alert box with the message: "Goodbye!". Because of
this, using a simple closure in a loop will end up with all closures sharing the same
variable and that variable will contain the last value assigned to it in the loop. For
example:
for (var i=0; i<10;
i++) {
document.getElementById('something'+i).onclick =
function(){alert(i)};
}
All elements when
clicked will generate an alert box with the number 10. In fact, if we now do
i="hello";
all elements will now generate a "hello" alert! The
variable i is shared across ten functions PLUS the current function/scope/context. Think
of it as a sort of private global variable that only the functions involved can
see.
What we want is an instance of that
variable or at least a simple reference to the variable instead of the variable itself.
Fortunately javascript already has a mechanism for passing a reference (for objects) or
value (for strings and numbers): function
arguments!
When a function is called in
javascript the arguments to that function is passed by reference if it is an object or
by value if it is a string or number. This is enough to break variable sharing in
closures.
So:
for (var i=0; i<10; i++) {
document.getElementById(i).onclick =
(function(x){ /* we use this function expression simply as a factory
to
return the function we really want to use: */
/* we want to return
a function reference
so we write a function expression*/
return
function(){
alert(x); /* x here refers to the argument of the factory
function
captured by the 'inner' closure */
}
/* The brace operators (..) evaluates an expression, in this
case this
function expression which yields a function reference.
*/
})(i) /* The function reference generated is then immediately
called()
where the variable i is passed */
}
No comments:
Post a Comment