This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
Given this:
var m = 5;
for (i = 0; i < m; i++) {
add_row('row-'+(i+1), () => f(i))
}
if I do an alert in my f the it will always output the value 5. I believe this is due to the same problem mention here for python:
lambda function don't closure the parameter in Python?
How is this problem solved in javascript?
This is because add_row() is asynchronous. It calls the callback you pass it sometime LATER when the operation finishes. Meanwhile, the for loop has already finished. One simple solution in ES6, is to use let with your for loop. This creates a new and separate i variable for each run of the loop so when the callback is called sometime LATER, the variable i for that invocation of the loop is still at it was when the function was started.
If you were to insert some console.log() statements into your code like this:
var m = 3;
console.log("A");
for (i = 0; i < m; i++) {
console.log("B" + i);
add_row('row-'+(i+1), () => {
console.log("C" + i);
f(i);
})
}
console.log("D");
What you would see in the console is this:
A
B0
B1
B2
D
C0
C1
C2
Notice how "D" comes before any of the "Cx" lines. That shows you how the asynchronous callback is called LATER after your for loop is done executing.
Please study that and when you fully understand the reasoning for that order, then you will finally understand an asynchronous callback. Technically, the C0, C1 and C2 will be at the end, but could be in any order relative to each other.
Here's how you can use let for the for loop to create a separate variable i for each iteration of the loop so you will still have the appropriate value of i when the callback is called some time later:
for (let i = 0; i < m; i++) {
add_row('row-'+(i+1), () => f(i))
}
Before ES6, one could fix this by creating an extra closure which creates a new function scoped variable to "remember" the loop index separately for each invocation:
for (var i = 0; i < m; i++) {
(function(index) {
add_row('row-'+(index+1), () => f(index))
})(i);
}
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
I have this code.
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
I don't understand the output from this lines of code:
The output in the console is number 6,and it says that is repeated five times.
If i use the let keyword for "i" then i get the output that i expect,
1,2,3,4,5 after one second
Why is that ?
var has scoping issues and that's why let was introduced.
In your for loop you are defining i, but actually it's stuck to the global scope, and after 1 second, the for loop would actually be done, and when the setTimeout callback is fired, i would have already reached 6 and it's read from the global scope.
In a nutshell, because i is stuck to the upper scope of for, each iteration modifies the calue of i and doesn't create another i.
If you change var to let, the issue is resolved.
setTimeout is async hence will execute after the loop is done, that s why you have i=6, put it in a self invoking function to retain the value of i or use let instead of var in your loop
for (var i = 1; i <= 5; i++) {
((i) => setTimeout(function() {
console.log(i);
}, 1000))(i)
}
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
I am in the process of learning JavaScript. While I completely understand that JavaScript does only have function scope and not block scope, I have stumbled accross an example of a for loop in which functions are created completely and I completely ignore why the following example does not work like one would expect it to.
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
}
It outputs this:
My value: 3
My value: 3
My value: 3
And not:
My value: 0
My value: 1
My value: 2
Now I know that the solution would be something like this would be to create a function that returns a function returning the i value which would create a closure for said function.
However, my question is rather, and I have seen a couple of explanations which I don't quite get, why the example does not work in the first place and what is actually happening?
I am unsure how the array func is filled with three keys:
func[0], func[1] and func[2] while the function declaration will always return 3? Why would func[0] even exist in the first place and not only func[3]? It seems to me that there some kind of scope is created in the for loop but only for assigning a key but not for the assignment which I find odd.
Could someone enlighten me? How would a step by step evaluation of the for-loop look like and why?
Thank you very much for your help already!
func[0], func[1] and func[2] while the function declaration will always return 2?
No. func[i] always returns 3, because i is 3 when the function runs.
It seems to me that there some kind of scope is created in the for loop
There isn't (well, there is, but let isn't in play so it isn't used for anything). The scope is defined by the enclosing function. i exists in that scope and gets changed by the for loop.
How would a step by step evaluation of the for-loop look like and why?
var funcs = [];
Create an array
for (var i = 0; i < 3; i++) { // let's create 3 functions
Sets i to 0, 1, 2, 3.
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
Assigns a function to the array at whatever i is currently
}
End of loop
for (var j = 0; j < 3; j++) {
Sets j to 0, 1, 2, 3.
i is still 3.
funcs[j](); // and now let's run each one to see
Calls the first function. i is 3.
Calls the second function. i is 3.
Calls the third function. i is 3.
}
Fin.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Calling functions with setTimeout()
(6 answers)
Closed 6 years ago.
I am trying to set a delay in the for loop in javascript. I want it to log i, have a delay, then log i, and so on. My question is, why does the following code work, having a function return a function as can be seen below:
for (var i = 1; i <= 5; i++) {
var tick = function(i) {
return function() {
console.log(i);
}
};
setTimeout(tick(i), 500 * i);
}
And the following code not work as expected:
for (var i = 1; i <= 5; i++) {
var tick = function(i) {
return console.log(i);
};
setTimeout(tick(i), 500 * i);
}
It prints out all the values in the for loop at once. Could someone please explain why this happens?
In both pieces of code, tick(i) is executed immediately in each loop
The difference is, that in the first example, tick(i) returns a function, which is called by setTimeout, and that outputs to the console
In the second example, the console.log is called immediately, undefined is returned, and setTimeout does nothing once the timeout has triggered as the given argument is not a function (or a string to be eval'd)
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
Say I have this function:
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
setTimeout( function() {
console.log( fruits[i]);
}, i * 1000);
}
}
printFruits(["Lemon", "Orange", "Mango"])
So this returns undefined 3 times.
I can see on a high level that since variables are stored not by value but by reference inside the closure... the loop is finishing first and by the time the functions are dequeued from maybe the Event Loop... the variable is already at undefined (fruits.length evaluates to 3 which is too high for this array size). But why does this perform strangely... it prints "apple" 3 times.
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
var someConstant = i;
setTimeout( function() {
console.log( fruits[someConstant]);
}, someConstant * 100);
}
}
printFruits(["mango", "banana", "apple"])
Shouldn't someConstant be changing as well with i? Why does it seem to be 2 always?
Also this works:
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
(function() {
var current = i;
setTimeout( function() {
console.log( fruits[current]);
}, current * 1000);
})();
}
}
Why is the IIFE necessary to fix this problem?
2nd Example
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
var someConstant = i;
setTimeout(function() {
console.log(fruits[someConstant]);
}, someConstant * 1000);
}
}
printFruits(["Lemon", "Orange", "Mango"])
This logs thrice Mango. Because everytime the someConstant variable is created and re-initialised to i. Recollect how for loop works. i-value is increasing here till 4, checks the condition 4<3, and terminates. So the matter inside the loop executes only thrice. So the last value of someConstant defined in the printFruits functional scope is 2. So when the inner function executes someConstant, its equal to 2. So we get each time Mango.
3rd example
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
(function() {
var current = i;
setTimeout(function() {
console.log(fruits[current]);
}, current * 1000);
})();
}
}
printFruits(["Lemon", "Orange", "Mango"])
Here the beauty of closures happening. Here its a self executing function being executed, immediately. So when i = 1, it invokes immediately. Every function has a different scope now. There is a seperate current value defined for each. So later when it executes, it recollects whats the value of 'current' when it was defined inside its scope.
The only difference between these samples is that the for loop increments i to 3 before stopping, while the last value which is assigned to someConstant is 2. Your "working" code is outputting Mango three times (index 2 of the array) instead of undefined (index 3 of the array). The general behaviour is the same.
Yes, you do need an IIFE, or ES6's let keyword instead of var.
The difference is that someConstant never gets incremented after the last iteration. The for() loop sets i = 3, the test i < fruits.length fails, so the loop stops. As a result, someConstant is still set to 2 from the last iteration of the loop. Then all the callbacks run, so they all log fruits[2], which is Mango.
You need the IIFE to get each iteration to save its value of i in the closure.
This uses var
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 10
This uses let
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 6
I don't understand why the result is different. Can somebody guide me?
The resulting array consists of functions, each function body looks like this:
console.log(i);
The value of i depends on whether we used var or let to declare the variable.
var (ECMAScript 5 and 6)
Here i is a global variable whose value is 10 after exiting the loop. This is the value that is logged.
let (ECMAScript 6)
Here i is a local variable whose scope is restricted to the for statement. Moreover, this variable gets a fresh binding on each iteration. This is best explained by your code transpiled to ECMAScript 5:
"use strict";
var a = [];
var _loop = function(i) {
a[i] = function() {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
a[6](); // 6
So, on seventh iteration for example, the value of i will be 6 (counting from zero). The function created inside the iteration will refer to this value.
I think it would be much better to not define functions in a loop, you could easily accomplish this with one function definition that returns a closure:
function logNumber(num) {
return function() {
console.log(num);
}
}
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = logNumber(i);
}
a[6]();
Regarding the difference between the two examples, one is using let for block scoping. A better example that shows the difference would be:
ECMA5:
for (var i = 0; i < 10; i++) { }
console.log(i); // 10
ECMA6:
for (let i = 0; i < 10; i++) { }
console.log(i); // i is not defined
Edit: as I stated in my comment to your question, this is more likely a side-effect of the transpiler you are using. Firefox supports block scoping and both versions of your loop produce 10 as output (which they should).
This is correct behavior according to the spec. The behavior with var and let is defined to be different.
See the spec, at https://people.mozilla.org/~jorendorff/es6-draft.html#sec-forbodyevaluation. According to this, the relevant concepts, which make the function declared inside the loop close over the current value of the block-scoped loop index, are things called "per-iteration bindings" and "per-iteration environment".
Babel handles it correctly, producing the following code:
var a = [];
var _loop = function (i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
This implements the semantics of for (let by isolating the contents of the for loop into a separate function parameterized by the index. By virtue of doing that, the function no longer closes over the for loop index, and i is treated separately in each function created. Thus the answer is 6.
Traceur does not produce the correct result. It yields 10.
So the famous question that has been asked 100 times on SO, about why my function declared in a loop and closing over the index index is using the "wrong" value of the loop index, shall be asked no more?
The issue is a bit more nuanced that merely proclaiming that "of course, let is block-scoped". We know that. We get how it works in an if block, for example. But what's going on here is a bit of an twist on block scoping in the context of a for, hitherto unknown to many people including me. It's a variable actually declared outside the "block" (if you think of the block as the body of the for statement) but has a separate existence inside each iteration of the loop.
For more, see https://github.com/babel/babel/issues/1078.
Why is result different in ES6 and ES5?
Because let and var are different. let is block-scoped while var is function-scoped.
In your first example there is only a single variable i. Every function you create has a reference to the same variable i. At the moment you call a[6](), i has the value 10, because that was the termination condition for the loop.
In the second example, every iteration of the loop has it's own variable i. It works exactly like in other languages with block scope.