I am trying to understand closures in JavaScript and came across this example:
for (let i = 0; i < 3; i++) {
const log = () => {
console.log(i);
};
setTimeout(log, 100);
}
Since the variable i
is scoped to the for
loop, a new reference is being given to it in each iteration, so it is not shared between iterations, so the closure captures the log
function along with the reference to the i
variable in each iteration.
as expected the output is:
$ 0
$ 1
$ 2
However, in the following snippet the behavior is different:
for (let arr = [1, 2, 3]; arr.length !== 0; arr.pop()) {
const log = () => {
console.log(arr);
};
setTimeout(log, 1000);
}
The expected output is
$ [1, 2, 3]
$ [1, 2]
$ [1]
But i got 3 logs with an empty array, this suggests that the arr
variable might be shared between iterations.
Your arr
variable is initialized on each iteration of the loop to a reference to the array. It is indeed a different variable on each iteration, but there's only one array, and your loop pops a value off on every iteration. Thus by the time the timers fire, the array is empty.
The precise details of how a locally-scoped variable in a for
loop works are kind-of complicated. The initialization expression is evaluated once (I'm pretty sure; the spec is hard to read), and then on each iteration a new variable is created and initialized to the value of the previous one. Thus, the direct value of the variable doesn't change in the case of an array reference. If you could do a "last_variable === this_variable" test somehow (you can't), it would be true
between iterations, because it's the same array. But with a primitive value, it wouldn't be the same.