Please explain the use of JavaScript closures in loops

I read a number of explanations about snapping and snapping inside loops. Itโ€™s hard for me to understand the concept. I have this code: is there a way to reduce the code as much as possible, so the concept of closing can be clearer. I can hardly understand the part in which i is inside two brackets. 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; 
+39
javascript scope closures loops
Aug 26 '10 at 6:16
source share
5 answers

WARNING: long (ish) answer

This is copied directly from the article I wrote on the company's internal wiki:

Question: How to use closures in a loop? Quick answer: use the factory function.

  for (var i=0; i<10; i++) { document.getElementById(i).onclick = (function(x){ return function(){ alert(x); } })(i); } 

or 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 is often confused by people who are not familiar with javascript or functional programming. This is the result of a misunderstanding of what closure is.

Closing doesn't just pass the value of a variable or even a reference to a variable. Closing captures a variable! The following bit of code illustrates this:

  var message = 'Hello!'; document.getElementById('foo').onclick = function(){alert(message)}; message = 'Goodbye!'; 

Clicking on the 'foo' element will create a window with the message: "Goodbye!". Because of this, using a simple closure in a loop will result in all locks using the same variable, and this 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 pressed will generate a warning window with the number 10. In fact, if we now do i="hello"; , all elements now generate a hi warning! The variable I is divided into ten functions. PLUS - current function / area / context. Think of it as some kind of private global variable that only functions can see.

We need an instance of this variable, or at least a simple reference to the variable instead of the variable itself. Fortunately, javascript already has a mechanism for passing a link (for objects) or a value (for strings and numbers): function arguments!

When a function is called in javascript, the arguments to this function are passed by reference if it is an object or value, if it is a string or number. This is enough to separate the distribution of variables in the closure.

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 */ } 
+83
Aug 26 '10 at 6:40
source share
โ€” -

I have been programming in JavaScript for a long time, and closing in a loop is a very broad topic. I assume that you are talking about the practice of using (function(param) { return function(){ ... }; })(param); inside a for loop to preserve the "current value" of the loop when this internal function is executed later ...

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 at the output, all internal callback functions all point to the same i , however, since each of them has its own โ€œclosureโ€, the value x actually stored like any i was during the execution of the external function.

Usually, when you see this template, you should use the same variable name as the parameter and the argument of the external function: (function(i){ })(i) for example. Any code inside this function (even if it is executed later as a callback function) will refer to i at the moment you called the "external function".

+7
Aug 26 '10 at 6:35
source share

Well, the โ€œproblemโ€ with closing in this case is that any access to i will refer to the same variable. This is due to the ECMA-/Javascripts function scope or lexical scope .

Therefore, to each call alert(i); displayed 5 (because after the loop is over i === 5) you need to create a new function that starts at runtime.

To achieve this, you need to create a new function, plus you will need additional parenthesis at the end, before invoke the outer function immediately, so link.onclick now returns the returned function as a link.

+3
Aug 26 '10 at 6:39
source share

A closure is a construct in which you reference a variable outside the scope in which it is defined. Usually you talk about closure in the context of a function.

 var helloFunction; var finished = false; while (!finished) { var message = 'Hello, World!'; helloFunction = function() { alert(message); } finished = true; } helloFunction(); 

Here I define the message of a variable and define a function that refers to the message. When I define a function to use the message, I create a closure. This means that helloFunction contains a link to the message, so that I can continue to use the message, even outside the area (loop body) where the message is defined.

Adding

In brackets (i) there is a function call. What's happening:

  • You define some function (num) {}. This is called an anonymous function because it is defined internally and has no name. Function
  • (num) takes an integer argument and returns a link to another function, which is defined as alert (num)
  • An external anonymous function is immediately called with argument i. So num = i. The result of this call is a function that will alert (i).
  • The end result is more or less equivalent: link.onclick = function() { alert(i); }; link.onclick = function() { alert(i); };
+2
Aug 26 '10 at 6:30
source share

To answer the last part of your questions. Two brackets call a function like any other function. Why are you doing this here, is that you want to keep the fact that the variable "i" is at that time. So, what does it do, the function is called, I am sent as the argument "num". Since it is called, it will remember the value of nume in the variable links of its own scoop.

If you havenโ€™t done so, click on the link โ€œ5โ€

John Resig, the founder of jQuery, has a really nice online presentation explaining this. http://ejohn.org/apps/learn/

.. Fredrick

0
Aug 26 '10 at 6:38
source share



All Articles