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