Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

JavaScript 閉包與作用域問題解題解析:如何正確輸出 1 2 3

最後更新:2024年11月4日JavaScript

在 JavaScript 中,for 迴圈與 setTimeout 的組合,經常會引發閉包與作用域的問題,導致結果與預期不符。

這篇文章將探討這種情境,並介紹兩種解決方法,使得以下程式碼能夠正確輸出 1 2 3。


問題說明(在不能將 var 修改成 let 的條件下)

以下代碼的目標是讓程式輸出 1 2 3:

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 0)
}

console.log(i);

在解決這個問題時,我們必須在不更動 var 宣告的情況下達到正確輸出。

通常,在 JavaScript 中使用 let 來宣告變數可以限制其作用域至每次迴圈內部,但這裡不得更動 var。

因此,需要其他方式,使每次迴圈中的 i 值在異步操作時不受後續迴圈影響。

當這段代碼執行時,console.log(i) 的結果並非 1 2 3,而是 3 3 3

原因在於,for 迴圈中的 var i 是一個全域變數,每次迴圈中並未將 i 綁定到該次迴圈。

因此 setTimeout 在迴圈結束後才執行,最終輸出重複的值。


解決思路 1:使用 bind 綁定變數

解決方案

為了解決這個問題,可以使用 bind 方法,將當前的 i 值綁定到一個新的函數中,確保 i 值在每次迴圈中獨立存在,並在異步操作執行時仍然保持該次迴圈的 i 值。

bind 方法的觀念

在 JavaScript 中,bind 方法會創建一個新的函數,並將特定的參數與函數綁定。

此方法可以用來綁定當前 i 值,保證每次迴圈中的 i 值獨立,避免受後續變數影響。

實作代碼

以下是使用 bind 方法綁定變數的程式碼:

for (var i = 0; i < 3; i++) {
  const aa = ((n) => {
    console.log(n);
  }).bind(null, i);

  setTimeout(aa, 0);
}

在這段程式碼中,我們將當前的 i 值作為參數傳遞給 bind 方法,創建了一個新的函數 aa,並綁定當前的 i 值給 n,使其在 setTimeout 執行時能輸出獨立的 i 值。

運行結果

透過 bind,我們為每次迴圈的 i 值創建了一個新的綁定,因此最終輸出將會是 1 2 3,因為每次執行 setTimeout 時,使用的是當前迴圈的 i 值副本。

延伸閱讀:JavaScript this 關鍵字解析|判定規則詳解


解決思路 2:使用 IIFE 綁定變數

解決方案

另一種解法是使用立即執行函數 (IIFE),為每次迴圈建立一個新的作用域,使得 i 值在每次執行時獨立存在,確保 i 不會受到後續迴圈的影響。

IIFE 的概念

立即執行函數 (IIFE, Immediately Invoked Function Expression) 是一種 JavaScript 表達式,可以在定義後立即執行。

IIFE 能夠建立新的作用域,在處理異步操作時可以捕捉當前值,避免後續變數的影響。

實作代碼

以下是利用 IIFE 綁定變數的程式碼:

for (var i = 0; i < 3; i++) {
  setTimeout(((n) => {
    console.log(n);
  })(i), 0);
}

在此解法中,我們使用 IIFE 在每次迴圈中立即執行並傳入當前的 i 值。

這樣 n 值在 IIFE 作用域內保持不變,確保每次執行的 setTimeout 引用的是當前 i 的值。

運行結果

此方式通過 IIFE 捕捉每次迴圈的 i 值,並作為局部變數 n 使用,最終結果同樣是 1 2 3,因為 setTimeout 中引用的是當時 n 的獨立值。

延伸閱讀:初學者指南:深入了解 JavaScript 中的 IIFE(立即執行函式表達式)


結論

透過使用 bind 或 IIFE,能夠在不修改 var 為 let 的情況下正確捕捉每次迴圈的變數 i 值,解決 JavaScript 中常見的閉包問題。

理解並掌握這些解決方法,將有助於在 JavaScript 中更有效地控制變數作用域。

目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

JavaScript

目錄

  • 問題說明(在不能將 var 修改成 let 的條件下)
  • 解決思路 1:使用 bind 綁定變數
  • 解決方案
  • bind 方法的觀念
  • 實作代碼
  • 運行結果
  • 解決思路 2:使用 IIFE 綁定變數
  • 解決方案
  • IIFE 的概念
  • 實作代碼
  • 運行結果
  • 結論