こんにちは、戦うエンジニア応援団長🔥です。
いつもはXで問題を出題していますが、ブログではさらに深掘り解説を行います!
なお、解答はX公開前にこちらで先行掲載中です。
❓ 本日の問題
// 問題:配列の各要素を1秒ごとに順番に出力したいのに、
// 以下のコードでは“すべて同時にundefined”となります。
// 原因と解決方法を答えよ。
const items = ['A', 'B', 'C', 'D'];
for (var i = 0; i < items.length; i++) {
setTimeout(function() {
console.log(items[i]);
}, 1000 * i);
}
// 結果:undefined × 4

🔍 解答 & 解説
// 解答例:var を let に変更してループごとにスコープを固定
const items = ['A', 'B', 'C', 'D'];
for (let i = 0; i < items.length; i++) {
setTimeout(function() {
console.log(items[i]);
}, 1000 * i);
}
// 結果:1秒ごとに A, B, C, D と順番に出力される
var
は関数スコープ、let
はブロックスコープ:ループごとにiが固定される- クロージャとイベントループの関係:コールバック実行時に参照されるiの値が変わらない
- 代替案:IIFE でiをキャプチャする方法もある
// IIFE で i をキャプチャ for (var i = 0; i < items.length; i++) { (function(idx) { setTimeout(function() { console.log(items[idx]); }, 1000 * idx); })(i); }
💡 深掘り解説
JavaScript のイベントループは、タスクキュー(マクロタスク)と Microtask の順序で実行されます。
`setTimeout` のコールバックはマクロタスクとして登録されるため、ループ終了後にまとめて実行される仕組みです。
– var:同一関数内で変数が共有されるため、ループ外での最新値を参照
– let/const:ループごとに新しい識別子を作成し、正しい値をクロージャに閉じ込める
– Temporal Dead Zone:`let` 宣言時に一時的にアクセス不可のゾーンが存在し、安全性が向上
さらに、Promise
や async/await
を併用すると、より細かい制御が可能です。 例えば、以下のように非同期関数内でループを回せば、より直感的に順序を保証できます:
async function outputSequentially(arr) {
for (let i = 0; i < arr.length; i++) {
await new Promise(res => setTimeout(res, 1000));
console.log(arr[i]);
}
}
outputSequentially(['A','B','C','D']);
まとめ
今回は var
→ let
の置き換えだけでなく、
JavaScript の実行モデルにも踏み込んで解説しました。 次回はさらにチャレンジングな課題を用意しますので、お楽しみに!
戦え、エンジニア諸君!!🔥
🔔 最新の問題・解説をいち早くキャッチしたい方は、 @Techgadget63915 をフォロー! Follow @Techgadget63915