Please Explain the Use of JavaScript Closures in Loops

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

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]();
}

How do JavaScript closures work?

A closure is a pairing of:

  1. A function and
  2. A reference to that function's outer scope (lexical environment)

A lexical environment is part of every execution context (stack frame) and is a map between identifiers (i.e. local variable names) and values.

Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to "see" variables declared outside the function, regardless of when and where the function is called.

If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.

In the following code, inner forms a closure with the lexical environment of the execution context created when foo is invoked, closing over variable secret:

function foo() {
const secret = Math.trunc(Math.random() * 100)
return function inner() {
console.log(`The secret number is ${secret}.`)
}
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`

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).

Javascript infamous Loop issue?

Quoting myself for an explanation of the first example:

JavaScript's scopes are function-level, not block-level, and creating a closure just means that the enclosing scope gets added to the lexical environment of the enclosed function.

After the loop terminates, the function-level variable i has the value 5, and that's what the inner function 'sees'.

In the second example, for each iteration step the outer function literal will evaluate to a new function object with its own scope and local variable num, whose value is set to the current value of i. As num is never modified, it will stay constant over the lifetime of the closure: The next iteration step doesn't overwrite the old value as the function objects are independant.

Keep in mind that this approach is rather inefficient as two new function objects have to be created for each link. This is unnecessary, as they can easily be shared if you use the DOM node for information storage:

function linkListener() {
alert(this.i);
}

function addLinks () {
for(var i = 0; i < 5; ++i) {
var link = document.createElement('a');
link.appendChild(document.createTextNode('Link ' + i));
link.i = i;
link.onclick = linkListener;
document.body.appendChild(link);
}
}

Loops and closures. For and Var

ES6's let is block scope that means it has it's own scope inside {} like many other traditional languages. But in contrast var is a global variable in your code.

In first for loop, function is just assigned to func[i] 3 times with final value 3 but not executed. If you execute the function inside first loop, you will get the expected output though like:

var funcs = [];for (var i = 0; i < 3; i++) {      // let's create 3 functions  funcs[i] = function() {          // and store them in funcs    console.log("My value: " + i); // each should log its value.  };  funcs[i](); // execution of func}

Please explain closures, or binding the loop counter to the function scope

This is done because JavaScript only has function scope, not block scope. Hence, every variable you declare in a loop is in the function's scope and every closure you create has access to the very same variable.

So the only way to create a new scope is to call a function and that is what

(function(i){/* Some code using i */}(i))

is doing.

Note that your example misses an important part: The immediate function has to return another function which will be the click handler:

someArray[i].onclick = (function(i){
return function() {
/* Some code using i */
}
}(i));

The immediate function is nothing special. It is somehow inlining function definition and function call. You can replace it by a normal function call:

function getClickHandler(i) {
return function() {
/* Some code using i */
}
}

for(var i=0; i < someArray.length; i++){
someArray[i].onclick = getClickHandler(i);
}

Javascript: closure of loop?

for(var i = 0; i < 10; i++) {
(function(i) {
createButton(function() { alert("button " + i + " pressed"); });
})(i);
}

Note that JSLint doesn't like this pattern. It throws "Don't make functions within a loop.".

Live demo: http://jsfiddle.net/simevidas/ZKeXX/

javascript closure advantages?

I think the best phrase to sum up the purpose of closures would be:

Data Encapsulation

With a function closure you can store data in a separate scope, and share it only where necessary.

If you wanted to emulate private static variables, you could define a class inside a function, and define the private static vars within the closure:

(function () {
var foo;
foo = 0;
function MyClass() {
foo += 1;
}
MyClass.prototype = {
howMany: function () {
return foo;
}
};
window.MyClass = MyClass;
}());


Related Topics



Leave a reply



Submit