0

I've been reading Jon Skeet's article on closure capturing strategies and want to explore what's the case with JavaScript.

arr = [];
for(let i = 0; i < 3; i++) {
    arr.push(() => console.log(i));
}
arr.forEach(ele => ele());



arr = [];
let j;
for(j = 0; j < 3; j++) {
    arr.push(() => console.log(j));
}
arr.forEach(ele => ele());

Surprisingly, the first prints 0,1,2 and the second 3,3,3.

I also found that JS always captures variables. So why the first case?

wlnirvana
  • 1,811
  • 20
  • 36
  • "*Surprisingly, the first prints 3,3,3 and the second 0,1,2.*" it's the opposite? Also see [Javascript infamous Loop issue?](https://stackoverflow.com/q/1451009) – VLAZ Jun 09 '22 at 15:22
  • 3
    let is scoped to the block. You are declaring let in a for block vs outside the block https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let – epascarello Jun 09 '22 at 15:23
  • @VLAZ Oh sorry my bad. Just fixed the wording. – wlnirvana Jun 10 '22 at 03:50
  • @epascarello I understand the scoping difference but am still confused about how capturing works. Please see [my comment](https://stackoverflow.com/questions/72562759/arrow-function-capture-strategies-for-let-declared-variables#comment128191371_72563315) below. – wlnirvana Jun 10 '22 at 04:29
  • 1
    Like C#, JavaScript captures the variable, not the value. The difference between the snippet is that in the first, there are three `i` variables which are not updated (one for each iteration of the loop body), while the in the second there is only one variable that gets closed over. – Bergi Jun 10 '22 at 05:32
  • @Bergi Thanks for the clarification as well as [your other answer](https://stackoverflow.com/a/30900289) linking to the spec. Now I see why in my first snippet JS captures variables but still behaves differently from C#. – wlnirvana Jun 10 '22 at 06:10

1 Answers1

1

arr = [];
for(let i = 0; i < 3; i++) {
    arr.push(() => console.log('1', i));
}
arr.forEach(ele => ele());



arr = [];
let k;
for(k = 0; k < 3; k++) {
    arr.push(() => console.log('2', k));
}
arr.forEach(ele => ele());

I've added the strings 1 and 2 inside the console.logs so that you can better see which one is actually going first.

The first to end is 1 and it works as expected because the let keyword is scoped to the for loop. On the other hand, console.log in the second loop takes k which is scoped in the window scope, thus when console.log runs, the value of k is going to be set to that of the end of the for loop, while the value of i is only valid and scoped (thus the closure) inside the first for loop.

  • 1
    So JavaScript has different capturing strategies when a variable is scoped differently? The `0,1,2` example seems similar to what [Jon Skeet](https://stackoverflow.com/a/4732617) and [Brian Goetz](https://stackoverflow.com/a/24145921) calls "capture values". And the behavior is clearly different from what C# does in [this example](https://dotnetfiddle.net/K7QJXu). – wlnirvana Jun 10 '22 at 04:11