Tuesday, June 4, 2024
 Popular · Latest · Hot · Upcoming
108
rated 0 times [  114] [ 6]  / answers: 1 / hits: 36260  / 14 Years ago, thu, august 26, 2010, 12:00:00

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;

More From » loops

 Answers
11

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 */
}

[#95804] Tuesday, August 24, 2010, 14 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
mckinleyi

Total Points: 121
Total Questions: 100
Total Answers: 109

Location: Peru
Member since Fri, Oct 14, 2022
2 Years ago
;