I'm not sure why I'm getting an error on the following snippet (adapted from JavaScript closure inside loops – simple practical example):
var funcs = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = (function(n) { // and store them in funcs
console.log("My value: " + n); // each should log its value.
})(i);
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
It seems like this should run ok; I know this is just something I don't fully get.
Here is the errror I get:
thx for any help
You need to return a function, rather than a result of the function. Try:
funcs[i] = (function(n) {
return function() {
console.log("My value: " + n);
}
})(i);
Example:
> var funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = (function(n) {
return function() {console.log("My value: " + n);}
})(i);
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
My value: 0
My value: 1
My value: 2
Related
I have a code sample
for(var i = 0; i < 3; i++) {
setTimeout() {
console.log("i " + i)
}
}
This is printing "3" to the console three times. However, I want it to print "0, 1, 2" without using let.
So far, I have tried the following -
var funcs = [];
function createfunc(i) {
return function() {
console.log("i ", i)
}
}
for(var i = 0; i < 3; i++) {
setTimeout(() => {
funcs[i] = createfunc(i) // statement 1
}, 1000)
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
This is working fine if I use the statement 1 just as it is without putting it into the setTimeout function. However, it is throwing an error if I'm using the setTimeout function. Can someone let me know how can I make print 0, 1, 2 to the console using the setTimeout function ?
It happens because of closure, anyway you can use the below codes, then you will be getting the proper result that you want.
for(var i = 0; i < 3; i++) {
((i) => {
setTimeout(() => {
console.log("i " + i)
}, 0);
})(i);
}
Like was previously said here: JavaScript closure inside loops – simple practical example
The output for this code is always 3
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.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
// My value: 3
// My value: 3
// My value: 3
This happens is because there's no difference between the global i and the i inside the anonymous function of the loop. So that when funcs[i] gets called, its current value is already 3.
The classic way to solve this is via a closure.
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++) {
funcs[j](); // and now let's run each one to see
}
By wrapping the function creation in the createfunc(i) function, you ensure that the value of "i" is bound to an unchanging value outside the function.
One way to solve this and that I don't get is through the creation of a let variable.
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
funcs[0]();
funcs[1]();
funcs[2]();
funcs[3]();
By doing so, I'd expect the variable let to be only accessible inside the loop, hence at the time I'm calling the function i is still undefined.
Since it works, it looks like JS is able to assign the proper variable i while still in the for loop and at the time I invoke the function it has the proper value i assigned.
What am I missing?
This is a closure.
The function is defined inside the loop, so it has access to all the variables that exist inside the loop, and it closes over them and continues to have access to them so long as the function exists.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
Here is a piece of code :
// -------- Sample 1 ---------------
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
// --------- Sample 2 --------------
var fun = [];
for(var i = 0;i < 3; i++) {
fun[i] = function() {
console.log('values is '+ i);
}
}
for(var i = 0; i < 3; i++) {
fun[i]();
}
Both the sample codes are same except the variable names. But it's giving different output. The output of the above code is :
My value: 3
My value: 3
My value: 3
values is 0
values is 1
values is 2
Can any one figure out why its happening ?
DEMO
var is hoisted, so to the interpreter, the first part's i actually looks more like this:
var funcs = [];
var i;
for (i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
// At this point, the for loop has finished; `i` has the value of 3 now:
for (var j = 0; j < 3; j++) {
// Each function retrieves the value of i and displays it
// and since `i` is 3, each function displays 3:
funcs[j]();
}
In your second sample (which is somewhat invalid JS because i gets declared twice), i gets reassigned to values 0, 1, and 2 while looping over the function that displays i.
In continuation to this question, I tried the following code that uses the same variable in both the loops and I get the desired result. My question is, WHY ?
So the initial code is:
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.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // this will not give desired output
}
The outputs this:
My value: 3
My value: 3
My value: 3
Whereas, the intended output is:
My value: 0
My value: 1
My value: 2
Now, if I use variable 'i' (a global variable) in the second loop as well, the code looks 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.
};
}
for ( i = 0; i < 3; i++) {
funcs[i](); // this gives desired output
}
I get the intended output,
My value: 0
My value: 1
My value: 2
WHY?
Now, if I use variable 'i' (a global variable) in the second loop as
well ... I get the intended output ... WHY?
Because when you do the first executing loop:
for (var j = 0; j < 3; j++) {
funcs[j]();
}
the loop constructing the array has finished and the global variable i has the value 3 and therefore console.log(i) results in logging 3 to the console for each iteration of the first executing loop. When you do the second executing loop:
for ( i = 0; i < 3; i++) {
funcs[i]();
}
you for each iteration of the loop assign a new value to the global variable i and it is this new value that the console.log(i) will log to the console: 0, 1, 2.
How do you achieve the desired result in a simple way?
You can use let to achieve the desired result in a simple way:
"use strict"; // use of let requires strict mode
var funcs = [];
for (let i = 0; i < 3; i++) { // just replace var with let here
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Now the array will be constructed with functions having access to a local variable, which is assigned the value of i at the time of adding the function to the array.
Because the variable i is contaminated. In your snippets, The loops write to the same variable i and the functions reads it. Note that there is just one variable i in your code.
What you really need to get your intended result is called "module pattern", which wraps a function to keep a local copy of the variable in the loop:
for (var i=0; i<3; i++) {
(function (j) {
funcs[j] = function() { // and store them in funcs
console.log("My value: " + j); // each should log its value.
};
})(i);
}
You can read this article to get more information about "module pattern".
Hi I want to create several function which work similar but use a slightly different value. I cannot change the parameters because I use these for a callback of a library. My first idea:
for(i = 0; i < 3; i++) {
f = function() {
console.log(i);
};
}
This obviously copies a refence of i to the function but I rather want to value at that time so that each function outputs a different value. I appreciate your help.
you may try this way:
var f=[];
for(i = 0; i < 3; i++) {
f[i] = (function(index) {
return function() {
console.log("My value: " + index);
}
})(i);
}
working demo here