letで変数を宣言すると、反復サイクルごとに新しい変数を宣言。
各setTimeoutは異なる変数インスタンスを参照するため、最終的な出力は期待通りのカウントがされた数になる。
var変数には、ブロックレベルのスコープがない。
varを使用してforループで変数iを反復すると、すべてのsetTimeoutでiが同じ変数を指す。
なぜこうなるのか、というとsetTimeoutの実行タイミングによるもの。
実行されるのは通常のタスクキューの後。つまり、forループが終了した後に実行が開始。
変数にはループでカウントが増加された後の5が保存される。
結果、すべてのsetTimeoutコールバック関数は5を出力。
for (let j = 0; j < 5; j++) {
setTimeout(() => {
console.log(j);
});
}
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
});
}
関数の場合も基本的に同じ。
var変数には、ブロックレベルのスコープがない。
変数iのスコープは、doCount関数のスコープ。
doCountへの各呼び出しはクロージャーと呼ばれるものを形成。
それぞれのクロージャーは独立しており、互いに干渉しない。
なので、
クロージャーはiの値を保存し、doCount()によって返される関数を呼び出すたびに、doCountの値が別々にカウントがされ増える。
letの場合も、もちろん互いに干渉しない。別々にカウントされる。
function doCount() {
var i = 0;
return function () {
console.log(i++);
};
}
var v1 = doCount();
var v2 = doCount();
v1();
v2();
v1();
function doCountL() {
let i = 0;
return function () {
console.log(i++);
};
}
let l1 = doCountL();
let l2 = doCountL();
l1();
l2();
l1();
今回の場合、返り値をvarで受けるかletで受けるかは影響しない。合わせてあるだけで。
現在、varを使うことはあまり推奨されていない。
基本的にはletか、今回は使っていないがconstを使うほうがいい。