JavaScript迴圈和作用域
我的翻譯小站:ofollow,noindex">https://www.zcfy.cc/article/javascript-loops-and-scope
翻譯原文連結:https://flaviocopes.com/javascript-loops-and-scope/
JavaScript 有一個特點,也許會讓開發者頭痛, 是與迴圈和作用域相關的.
舉個例子:
const operations = [] for (var i = 0; i < 5; i++) { operations.push(() => { console.log(i) }) } for (const operation of operations) { operation() }
它基本是迴圈了5次,將一個函式新增到operations數組裡面。這個函式可打印出迴圈變數索引值i
.
執行這些函式後
期望的結果應該是:
但實際發生的是這樣的:
為什麼會有這種情況? 因為使用的是var
.
由於提升了var
變數, 上面的程式碼等同於
var i; const operations = [] for (i = 0; i < 5; i++) { operations.push(() => { console.log(i) }) } for (const operation of operations) { operation() }
因此,在for-of迴圈中, i
依然是可見的, 它等於5,並且每次在函式中涉及到i
,都將使用這個值。
那麼我們應該如何做讓其變成我們想的這樣呢?
最簡單的方案是用 let
宣告. 在ES2015中介紹到, 它們有很大的幫助,能避免關於使用var
宣告的一些奇怪問題。
簡單的在迴圈變數時將var
變成 let
,能夠很好的執行:
const operations = [] for (let i = 0; i < 5; i++) { operations.push(() => { console.log(i) }) } for (const operation of operations) { operation() }
這是輸出結果:
這是怎麼實現的呢?這是因為每次迴圈重複的時候,都將重新創造i
,同時每個函式新增operations
陣列時,能獲取它本身的i
。
記住你不能使用 const
在這種情況下, 因為這會導致for
在第二次迴圈時, 嘗試賦新值報錯。
另外一個非常普遍的解決這個問題是使用pre-ES6程式碼, 同時它被稱作即時呼叫函式表示式 (IIFE).
在這種情況下,你可以包裝整個函式,並將i
繫結在它上面。自這種方式,你正在創造一個能立即執行的函式,你從其返回的一個新的函式。因此我們可以稍後執行它。
const operations = [] for (var i = 0; i < 5; i++) { operations.push(((j) => { return () => console.log(j) })(i)) } for (const operation of operations) { operation() }