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:
- A function and
- 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
Best Way to Iterate Over an Array Without Blocking the Ui
Are Eval() and New Function() the Same Thing
Defining Methods via Prototype VS Using This in the Constructor - Really a Performance Difference
Variable as the Property Name in a JavaScript Object Literal
Is There Any Non-Eval Way to Create a Function with a Runtime-Determined Name
How to Convert an Array of Objects to Object with Key Value Pairs
How to Automate Shadow Dom Elements Using Selenium
Copy Array Items into Another Array
Nodejs VS Node on Ubuntu 12.04
How to Convert Url Parameters to a JavaScript Object
What Is the Shortest Function for Reading a Cookie by Name in JavaScript
Send Response to All Clients Except Sender
Javascript: Filter() for Objects
How to Prevent Enter Keypress to Submit a Web Form
How to Get the Text Node of an Element
Getting Around X-Frame-Options Deny in a Chrome Extension
How to Pass Data from a Page to Another Page Using React Router