forのインクリメントなどに使うletで定義した変数は、ループごとにスコープを持つ。
var examples = []; for(let i=0; i<3; i++) { examples.push(() => console.log(i)); } for (const elem of examples) { elem(); }
このiは、それぞれが独立したメモリ領域を持つとのこと。
従って、実行結果は
0 1 2
となる。
つまりは、こういうコードと同様の動作をする。
var examples = []; for(let i=0; i<3; i++) { let _i2 = i; examples.push(() => console.log(_i2)); } for (const elem of examples) { elem(); }
余談だが、 let i を var i に変えると
var examples = []; for(var i=0; i<3; i++) { examples.push(() => console.log(i)); } for (const elem of examples) { elem(); }
実行結果は
3 3 3
になる。(ループ終了判定時にiが3になるため)
混乱しやすいというか当然だが、
var examples = []; let i=0; for(; i<3; i++) { examples.push(() => console.log(i)); } for (const elem of examples) { elem(); }
とletをループの外側で定義すれば、結果は
3 3 3
になる。
結論
var examples = []; for(let i=0; i<3; i++) { examples.push(() => console.log(i)); }
は
var examples = []; for(let i=0; i<3; i++) { let _i2 = i; // ※ここでiで書いていてもforループ内ではjavascript側で_i2がlet変数として用意され、ループ内でiと記述していた箇所が_i2に自動で置き換わって処理されるイメージ★あくまでイメージ★ examples.push(() => console.log(_i2)); }
のシンタックスシュガーのようなものということになるのかな。それで割り切っていこうというお話でした。