前言
接續上篇內容,這篇將用幾個經典範例用來更深入了解閉包。
典型的例子:
1 | function buildFunctions() { |
做為人類,我們預期三個結果應該會是 0 、 1 、 2,但實際上卻是回傳 3 。
結合我們之前的觀念,當程式碼執行到 arr.push
時,匿名函式被創造,但是必須要注意的是在此時它並沒有被執行,只是把匿名函式放入陣列,接著回傳 arr
。
我們宣告變數 fs
指向 buildFunctions
函式,並且進行呼叫, for 迴圈執行完畢後 i
的值就是 3 被保存在 buildFunctions
函式的執行環境內。
而我們呼叫了匿名函式,由於函式內部沒有 i
變數,因此會轉而向外部尋找,此時雖然外部 buildFunctions
函式的執行環境不存在,但是因為閉包,所以仍然可以取得 i
的值,所以輸出才是 3 。
而其餘的呼叫也因為需要的變數都是 i
,所以指向相同的外部環境尋找變數,因此結果都是一樣的。
如何修正成為我們預期的結果?
如同我們在 IIFE 章節得出的結果,可以使用 ES6 新增的 let 來修改程式碼:
1 | function buildFunctions() { |
let 的範圍只有 for 迴圈的 {} 內,而每次進行迴圈時都會在執行環境內不同的記憶體位址建立 i
,所以當這個匿名函式被呼叫,每次都會指向不同的記憶體位址。
ES5 的作法 IIFE
1 | function buildFunctions() { |
原理很簡單,先前例子 i
的值會相同,是因為尋找到相同外部環境的同樣變數值。
那麼只要個別創造不同的執行環境保存變數 i
就解決問題了。 IIFE 可以有效地解決這個問題,因為只要函式被呼叫了,就會建立一個執行環境。
像這樣,每次進行迴圈時,都有一個 IIFE 被執行,建立了執行環境,保存當下 i
的值,然而當內部的匿名函式被呼叫時,不再需要跑到最外層去存找 i
,而是在 IIFE 那一層就可以找到相應的 i
。