varのとき
js
var messages = ["foo", "bar", "hoge"];
for (var i = 0; i < messages.length; i++) {
setTimeout(function() {
console.log(messages[i], i);
}, i * 100);
}
// undefined 3
// undefined 3
// undefined 3
letのとき
js
const messages = ["foo", "bar", "hoge"];
for (let i = 0; i < messages.length; i++) {
setTimeout(function() {
console.log(messages[i]);
}, i * 100);
}
// 'foo'
// 'bar'
// 'hoge'
挙動の違いについて
varを使用したほうの問題点は、iの値がすべて「3」になっている点です。
これには理由が2つあります、
1. varはブロックスコープを無視する
varはブロックスコープを無視して、もっとも近い関数スコープの直下に宣言部分が巻き上げられます。今回のfor文でも同様に、loop内でスコープを持っておらず、毎度iの値を上書きしてしまっていることになります。
2. for文の処理が完了後にcallback関数が発火される。
setTimeout内の関数は0ms,100ms,200ms後に発火するようにcallbackタスクキュー内に入り、その後に関数が発火します。関数が発火するタイミングでは、varの値がloop後の値になってしまい、結果iの値が3の状態ですべての関数が発火するので、undefinedが返ることになる。
letでは問題ない理由
letの場合、ブロックスコープを持ちます。for文でループが回るたびにスコープをもつことになるため、callback関数が参照するiの値もsetTimeoutで登録されたときの値になります。結果、意図した挙動になります。