ECMAScript6 let・ループ・ブロックスコープ
ES5以下がだいだい分かってきたような気がしたから、ES6に手を出していくことにした。
以下の挙動が意味不明だった。
var callback = []; for (let i = 0; i < 3; i++) { callback[i] = function () { console.log(i); }; } callback[0](); // 0 callback[1](); // 1 callback[2](); // 2
だいだい、letはブロックスコープ内で再定義できないはずなのに、何でループできてるのか…?
ついでに、let を var に変えるとこうなる。
var callback = []; for (var i = 0; i < 3; i++) { callback[i] = function () { console.log(i); }; } callback[0](); // 3 callback[1](); // 3 callback[2](); // 3
変数iはグローバル変数なので、関数実行時の値が参照される。
0, 1, 2 ... としたい場合、ES5なら以下のように書く。
var callback = []; for (var i = 0; i < 3; i++) { (function (i) { callback[i] = function () { console.log(i); }; }(i)); } callback[0](); // 0 callback[1](); // 1 callback[2](); // 2
即時関数に引数から変数iを渡す際、即時関数内で変数iが新たに定義される。
グローバルの変数iと、即時関数スコープ内の変数iは別物となる。
ついでに言うと、callback配列内の各関数はクロージャとなる。変数iは、各関数ごとに独立している。
話は初めのletに戻るが、そもそもループの解釈を勘違いしていた。
以下のように、ループ毎に、新たにブロックスコープが作られるイメージが正しいと思う。
{ let i = 1; function callback1() { console.log(i); } } { let i = 2; function callback2() { console.log(i); } } callback1(); // 1 callback2(); // 2
ループ毎にletが定義されていると考えると、初めの挙動にも納得がいく。