JavaScript Closure Inside Loops - Simple Practical Example

JavaScript closure inside loops – simple practical example

Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.

ES6 solution: let

ECMAScript 6 (ES6) introduces new let and const keywords that are scoped differently than var-based variables. For example, in a loop with a let-based index, each iteration through the loop will have a new variable i with loop scope, so your code would work as you expect. There are many resources, but I'd recommend 2ality's block-scoping post as a great source of information.

for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.



ES5.1 solution: forEach

With the relatively widespread availability of the Array.prototype.forEach function (in 2015), it's worth noting that in those situations involving iteration primarily over an array of values, .forEach() provides a clean, natural way to get a distinct closure for every iteration. That is, assuming you've got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});

The idea is that each invocation of the callback function used with the .forEach loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it's used in an asynchronous callback, it won't collide with any of the other callbacks established at other steps of the iteration.

If you happen to be working in jQuery, the $.each() function gives you a similar capability.



Classic solution: Closures

What you want to do is bind the variable within each function to a separate, unchanging value outside of the function:

var funcs = [];

function createfunc(i) {
return function() {
console.log("My value: " + i);
};
}

for (var i = 0; i < 3; i++) {
funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}

JavaScript closure inside loops – simple practical example

Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.

ES6 solution: let

ECMAScript 6 (ES6) introduces new let and const keywords that are scoped differently than var-based variables. For example, in a loop with a let-based index, each iteration through the loop will have a new variable i with loop scope, so your code would work as you expect. There are many resources, but I'd recommend 2ality's block-scoping post as a great source of information.

for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.



ES5.1 solution: forEach

With the relatively widespread availability of the Array.prototype.forEach function (in 2015), it's worth noting that in those situations involving iteration primarily over an array of values, .forEach() provides a clean, natural way to get a distinct closure for every iteration. That is, assuming you've got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});

The idea is that each invocation of the callback function used with the .forEach loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it's used in an asynchronous callback, it won't collide with any of the other callbacks established at other steps of the iteration.

If you happen to be working in jQuery, the $.each() function gives you a similar capability.



Classic solution: Closures

What you want to do is bind the variable within each function to a separate, unchanging value outside of the function:

var funcs = [];

function createfunc(i) {
return function() {
console.log("My value: " + i);
};
}

for (var i = 0; i < 3; i++) {
funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}

Closure Inside For Loop

Problem

It's not a Closure.

for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 10)
}

setTimeout expects actually a function as parameter, but you made it invoking immediately. So it logs immediately the value of i, without waiting. setTimeout has now the return of your anonymous function as first parameter which is undefined.

The same here:

for(var i = 0; i < 10; i++) {
setTimeout((function() {
console.log(i);
})(), 10)
}

It's executed immediately, it doesn't wait for the 10ms. The only difference you didn't passed i as parameter, but it will look in the parent scope for a variable called i – and there is one.

You will see that your anonymous function is called immediately if you set the time to e.g. one second (1000).

Solution

A real closure would look like this:

Without parameter: You will see 10 times 10, because at time of execution of the inner function the loop is already finished which means i equals 10 at that time:

for(var i = 0; i < 10; i++) {
(function() {
setTimeout(function() {
console.log(i);
},10);
})();
}

With parameter – you will get the expected result:

for(var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
},10);
})(i);
}

Javascript closure with for loop

The key problem you're facing is that a closure capture variables by reference, not by value. For example:

var i = 0;
var f = function(){ i++; console.log(i); }
var g = function(){ i++; console.log(i); }
f(); g(); f(); g();

this will display 1, 2, 3, 4 because f and g are not capturing the value of the variable i, but the variable itself and therefore are both mutating the same variable.

A very common mistake is to create multiple closures in a loop forgetting that all of them will capture the same variable (the one used in the loop) and thus that when this loop variable is incremented all the already created closures will see it changing. For example in your code

for (var i = 0; i < 3; i++) {
funcs[i] = function() {
var item = "item" + i; // inside
console.log("item: " + item + ", i: " + i);
};
}

item is a variable local to the function but i instead comes from the outside and it's shared between all of them. This can be seen for example with

i = 42;
funcs[0](); funcs[1](); funcs[2](); // Displays 42 three times
i = 99;
funcs[0](); funcs[1](); funcs[2](); // Now they all display 99

In the second case of your question things are just slightly different because in

for (var i = 0; i < 3; i++) {
var item = "item" + i;
funcs[i] = function() {
console.log("item: " + item + ", i: " + i);
};
}

also the item variable is now shared as it's defined outside the function and the closure captures it. In the display you see the last value that's assigned to it.

The key point to remember that in Javascript a variable is local to the function, not local to the {...} block (like it's in C++ for example) and thus the discriminating factor is if the variables are declared inside the function or outside the function, not if they're declared inside or outside the loop.

To overcome this common problem and have a separate index for each of the closures created in a loop often you will see code like

var f = (function(i){return function(){ ... };})(i);

where a function is created and immediately called to return another function.

The reason is that the only way to create a separate new variable is a function, leaving us with the pattern of creating a function that returns a function: the useful closure (the inner) in this case will capture a variable that is different for each of the iterations (because it's a parameter of the outer).

Changing your code to

for (var i = 0; i < 3; i++) {
funcs[i] = (function(i){return function() {
var item = "item" + i;
console.log("item: " + item + ", i: " + i);
};})(i);
}

Will work as expected (i.e. each function will now have its own i value).

Creating closures in loops

JavaScript has functional scope. That means all variables defined in a function are hoisted to the top of the function. C# or Java has block level scope so defining item in a loop is the natural way in those languages . That is not the case in JavaScript. To avoid confusion all variables should be declared at the top of the function. The JS interpreter will hoist var item to the top of the function when it evaluates it. In the loop you are setting onfocus to a function with a variable referencing item in the the parent function scope of setupHelp. At the end of the loop, item has the assigned value of the last indexed item. So every time onfocus executes the function it references the assigned item value in the setupHelp function scope. In order to create a closure you will need to execute an anonymous function in each iteration of the loop that will create a new functional scope. This new scope should have a var that is assigned the current item from the outer function.

This example shows a functional scope closure in each iteration of the for loop:

function showHelp(help) {
document.getElementById('help').innerHTML = help;
}


function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];

for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = (function() {
var saveItem = item;
return function () {
showHelp(saveItem.help);
}
})();
}
}

setupHelp();

Example fiddle:

https://jsfiddle.net/ohoy75kh/1/

Or

https://jsfiddle.net/ohoy75kh/2/

Please explain the use of JavaScript closures in loops

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

Explanation of `let` and block scoping with for loops

Is this just syntactic sugar for ES6?

No, it's more than syntactic sugar. The gory details are buried in §13.6.3.9
CreatePerIterationEnvironment.

How is this working?

If you use that let keyword in the for statement, it will check what names it does bind and then

  • create a new lexical environment with those names for a) the initialiser expression b) each iteration (previosly to evaluating the increment expression)
  • copy the values from all variables with those names from one to the next environment

Your loop statement for (var i = 0; i < 10; i++) process.nextTick(_ => console.log(i)); desugars to a simple

// omitting braces when they don't introduce a block
var i;
i = 0;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;

while for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i)); does "desugar" to the much more complicated

// using braces to explicitly denote block scopes,
// using indentation for control flow
{ let i;
i = 0;
__status = {i};
}
{ let {i} = __status;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;



Related Topics



Leave a reply



Submit