Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please explain the use of JavaScript closures in loops [duplicate]

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; 
like image 630
CMS scripting Avatar asked Aug 26 '10 06:08

CMS scripting


People also ask

What is the use of JavaScript closure?

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.

What are closures and promises in JavaScript?

What is a Closure then? A closure is formed when you nest functions, inner functions can refer to the variables present in their outer enclosing functions even after their parent functions have already executed.

Is closure unique to JavaScript?

Closures are a very powerful yet underused feature unique to of JavaScript (and other ECMAScript languages). They essentially provide your code with private variables that other scripts can't access.

Where are closures stored in JavaScript?

How do JavaScript closures work? At the virtual machine level, every function has its own lexical environment which keeps track of this information. The virtual machine finds which variables are accessed in closures and stores them on the heap, and they live for as long as any closure might need them.


2 Answers

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 */   } 
like image 200
slebetman Avatar answered Oct 13 '22 23:10

slebetman


I've been programming in JavaScript for a long time, and "closure in a loop" is a very broad topic. I assume you are talking about the practice of using (function(param) { return function(){ ... }; })(param); inside of a for loop in order to preserve the "current value" of the loop when that inner function later executes...

The code:

for(var i=0; i<4; i++) {   setTimeout(     // argument #1 to setTimeout is a function.     // this "outer function" is immediately executed, with `i` as its parameter     (function(x) {       // the "outer function" returns an "inner function" which now has x=i at the       // time the "outer function" was called       return function() {           console.log("i=="+i+", x=="+x);       };     })(i) // execute the "closure" immediately, x=i, returns a "callback" function   // finishing up arguments to setTimeout   , i*100); } 

Output:

i==4, x==0 i==4, x==1 i==4, x==2 i==4, x==3 

As you can see by the output, all of the inner callback functions all point to the same i, however, since each had its own 'closure', the value of x is actually stored as whatever i was at the time of the outer function's execution.

Commonly when you see this pattern, you would use the same variable name as the parameter and the argument to the outer function: (function(i){ })(i) for instance. Any code inside that function (even if executed later, like a callback function) is going to refer to i at the time you called the "outer function".

like image 24
gnarf Avatar answered Oct 13 '22 22:10

gnarf