Loops of the form for (let x…) create a fresh binding for x in each iteration.
It refers to the value kept in the memory.
Look at this example:
let arr = 'this';
for (let i in arr) {
console.log(i); // logs 0, 1, 2, 3
}
for (let i of arr) {
console.log(i); // logs t, h, i, s
}
Above works as one expects.
Now look at this:
let arr = 'this';
for (let i in arr) {
console.log(i); // logs 0, 1, 2, 3
window.setTimeout(function() { console.log(i) }, 1000); // logs 3, 3, 3, 3
}
What happens is this:
A value for i is allocated while inside the for loop block (N.B. inside the block there is ONLY one i)
- Iteration 1: i is set to 0, 1st function logs 0, 2nd function waits
for 1 second (i is 0)
- Iteration 2: i is set to 1, 1st function logs 1, 2nd function waits
for 1 second (i is 1), since the timeout for 1st iteration hasn’t
been reached the i in the 1st iteration timeout is also set to 1
- Iteration 3: i is set to 2, 1st function logs 2, 2nd function waits
for 1 second (i is 2), since the timeout for 1st & 2nd iteration
haven’t been reached the i in the 1st & 2nd iteration timeouts are also set
to 2
- Iteration 4: i is set to 3, 1st function logs 3, 2nd function waits
for 1 second (i is 3), since the timeout for 1st, 2nd & 3rd
iteration haven’t been reached the i in the 1st, 2nd & 3rd iteration timeouts
are also set to 3
- timeout 1: logs 3
- timeout 2: logs 3
- timeout 3: logs 3
- timeout 4: logs 3
There can be a situation when the timeout catches up with the non-timedout function (ie very short timeout or complex non-timedout function, which can result in even more unpredictable outcome.
To simplify, timeout inside a loop is not a good idea and should be avoided.
The same applies to var for the same reason. (N.B. inside the block there is ONLY one i)
let arr = 'this';
for (var i in arr) {
console.log(i); // logs 0, 1, 2, 3
window.setTimeout(function() { console.log(i) }, 1000); // logs 3, 3, 3, 3
}
The is no difference between var & let inside a block. The difference between them is only outside a block where variables set with let inside the block, are no longer available.
Just for fun …
let arr = 'this';
for (let i in arr) {
console.log(i); // logs 0, 1, 2, 3
delay(i);
}
function delay(i) { // this i changes with every iteration
window.setTimeout(function() { console.log(i) }, 1000); // logs 0, 1, 2, 3
}