This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I have code that looks like this:
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
}
}
Unfortunately, it seems that i is being passed by reference, so all the functions in a output 10. How do I make it so that each function outputs the value that i had when it was created? I.e. a[0]() gives 0, a[1]() gives 1, etc.
EDIT: to clarify, I do not want a to store the values 0-9. I want a to store functions that return the values 0-9.
You need to invoke a function (to create a closure that captures your value) which returns a function (the one you want to end up with). Something like this:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(value) {
return function() {
console.log(value);
}
})(i);
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = (function(j) {
return function () {
console.log(j);
}
})(i);
}
A better performing version -
function makeFunction(i) {
return function () {
console.log(i);
}
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = makeFunction(i);
}
JSFiddle Demo.
Related
This question already has answers here:
Let vs. var in a for loop
(1 answer)
How do JavaScript closures work?
(86 answers)
Explanation of `let` and block scoping with for loops
(5 answers)
I am confused with javascript let and var in for loop? [duplicate]
(1 answer)
Closed 1 year ago.
I do understand the javascript closure as I think and I did a lot of small programs with it but in this code what I didn't understand is When I call the functions in the array why they are printing out different values of "i" aren't they suppose to refer to the same "i"?
function makeFunctionArray() {
const arr = []
//let i = 0
for (let i = 0; i < 5; i++) {
arr.push(function () { console.log(i) })
}
//console.log(i)
return arr
}
let functionarr = makeFunctionArray()
functionarr[0]() //print out 0
functionarr[2]() //print out 2
functionarr[1]() //print out 1
When you do
for (let i = 0; i < 5; i++) {
arr.push(function () { console.log(i) })
}
The arr would have the values
[console.log(0),
console.log(1),
console.log(2),
console.log(3),
console.log(4)];
So when you return arr and then you call functionarr like this
functionarr[0]() //print out 0
functionarr[2]() //print out 2
functionarr[1]() //print out 1
It would return:
console.log(0);
console.log(1);
console.log(2);
REMEMBER : it is an scope issue. If you use var, when you call the i variable into the array function, it would return the last i value. Thats the reason of why it returns always 5. If you use let, it would return the local i value, so it would save [0, 1, 2, 3, 4].
Using let
const arr = [];
for (let i = 0; i < 5; i++) {
arr.push(
function () {
console.log(i); //i value at the moment that you create function would be saved
}
);
}
arr[0](); //print 0
Using var
const arr = [];
for (var i = 0; i < 5; i++) {
arr.push(
function () {
console.log(i); //final i value would be saved (current i value)
}
);
}
arr[0](); //print 5
This question already has answers here:
Why let and var bindings behave differently using setTimeout function? [duplicate]
(2 answers)
Closed 5 years ago.
I understand that let has block scope and var has functional scope. But I do not understand in this case, how using let will solve the problem
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
const arr = [1,2,3,4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
This is all related to the scope of the variable. Let's try to wrap both the pieces into functions, and observe the output:
function test() {
// `i` will be declared here, making it a non-for-loop scoped variable
const arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
}
test();
So in the first case, i will be hoisted, and because of the asynchronous nature of setTimeout, i will immediately become 4 as the loop ends without waiting. This will make arr[i] to point to an undefined element in the array.
In the second case, i is not hoisted, and has scoped access to each iteration of the loop, making i accurately available to console.log statement. Thus the results are as per the expectations:
function test() {
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
}
test();
First of all, the output will be four times and not five times(as mentioned in your comment).
I pasted your code in Babel REPL and this is what I got,
"use strict";
var arr = [1, 2, 3, 4];
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
Do you see how let works internally now? :-)
You can still use var for setTimeout. You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout such that the value of i is recognised by the setTimeout function.
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
(function(i){
setTimeout(function() {
console.log(arr[i])
}, 1000)})(i);
}
This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?
This question already has answers here:
JavaScript for loop index strangeness [duplicate]
(3 answers)
Closed 8 years ago.
There's some code, and it's not working.
window.onload = function ()
{
var div = document.getElementById ('main');
var img = div.children;
var i = 1;
//console.log(img[i]);
for (var i=1; i != img.length; i++)
{
img[i].onclick = function ()
{
console.log(img[i]);
}
}
}
Please, explain me Why is img[i] in console.log(img[i]); undefined ?
How this bug can be fixed?
why i!=img.length?
Try change to:
for (var i=0; i < img.length; i++)
{
img[i].onclick = function ()
{
console.log(img[i]);
}
}
You are declaring var two times, remove var 1 = 1;
And assign var i value to zero(0)
for (var i=0; i < img.length; i++)
{
img[i].onclick = function ()
{
console.log(img[i]);
}
}
Just when I thought I understood closures...
The following code snippet:
function f() {
var a = [];
var i;
for (i = 0; i < 3; i++) {
a[i] = function () {
var x = i;
return x;
}
}
return a;
}
var a = f();
console.log(a[0]());
console.log(a[1]());
console.log(a[2]());
prints out 3, 3, 3. I don't understand why. I'm copying the value of 'i' to the local variable x, so there should be three x's: x0=0, x1=1. x2=2. How are all of them reading the final value of i?
Your problem is caused by each a[i] being, in fact, a closure. They all share the same i, which is evaluated when each a[i] is called, not when the loop executes. You need to create each closure with a separate context. For instance:
function f() {
var a = [];
var i;
for (i = 0; i < 3; i++) {
a[i] = makeClosure(i);
}
return a;
}
function makeClosure(i) {
return function () {
var x = i;
return x;
}
}
Even though the value of i changes in your for loop, it's still the same i variable. You need to shadow i in that scope and effectively pass it by value:
for (var i = 0; i < 3; i++) {
(function(x) {
a[x] = function() {
return x;
}
})(i);
}