フォロー

📝【団長のデバッグ道場🔥 vol.9】LEVEL ★★☆☆☆ 問題&解答&深掘り

こんにちは、戦うエンジニア応援団長🔥です。
いつもは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 と順番に出力される
  1. var は関数スコープ、let はブロックスコープ:ループごとにiが固定される
  2. クロージャとイベントループの関係:コールバック実行時に参照されるiの値が変わらない
  3. 代替案: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` 宣言時に一時的にアクセス不可のゾーンが存在し、安全性が向上

さらに、Promiseasync/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']);

まとめ

今回は varlet の置き換えだけでなく、
JavaScript の実行モデルにも踏み込んで解説しました。 次回はさらにチャレンジングな課題を用意しますので、お楽しみに!

戦え、エンジニア諸君!!🔥

🔔 最新の問題・解説をいち早くキャッチしたい方は、 @Techgadget63915 をフォロー! Follow @Techgadget63915

コメントする