克服JS奇怪的部分_ 瞭解閉包(二)

Huang Pei
3 min readOct 27, 2019

--

Closure經典例題解析、利用IIFE保存值

本筆記出自:JavaScript 全攻略:克服 JS 的奇怪部分

function buildFunctions() {  var arr = [];    //i=3時停止推f進陣列並return arr
for (var i = 0; i < 3; i++) {
arr.push(function () {
console.log(i)})}
return arr; //返還陣列,此時i=3} var fs = buildFunctions();fs[0]() //3
fs[1]() //3
fs[2]() //3
執行buildFunction,i=3時arr已被推入了f0, f1, f2
buildFunction從執行堆疊被移除後,記憶體的i和arr都還存在,呼叫fs時裡面沒有i可被取用,因此向外曾尋找,此時記憶體裡被保留的i=3將會被取用

簡單而言,

i=0 推了一個f1進陣列,arr=[f0];(沒執行f0函數!)

i=1 再推一個f2進陣列,arr=[f0, f1];(沒執行f1函數!)

i=2 再推一個f3進陣列,arr=[f0, f1, f2];(沒執行f2函數!)

i=3 此時 i 超過了迴圈的範圍條件,因此停止推陣列的動作。

會讓人誤以為console.log為1, 2, 3是因為以為推完一個陣列就會執行裡面的f,然而實際上是i跑完了並返還了陣列和裡面的f,呼叫後才開始執行。

而執行陣列中的f時裡面沒有i,要向外層尋找,此時雖然buildFunction已經從執行堆疊被移除,但記憶體中保存的i=3還存在,因為指向的記憶體相同,因此無論哪個函式執行i的結果都會是3。

若要console.log列印出1, 2, 3,就需要改變執行環境。若將push的函式加上IIFE,執行迴圈時就會當下執行IIFE並傳入當下的 i,並在IIFE的記憶體空間內保存為 j ,而裡頭的匿名函式在被呼叫時,就可以使用被保存的 j 值。

(詳細可以看下圖與註記)

function buildFunctions() {  var arr = [];
for (var i = 0; i < 3; i++) {
arr.push(
(function(j){
return function () {
console.log(
j)}}
)(i) // 執行當下傳入的i值會被保留為j,裡面的匿名函式可以取用
)} return arr;}var fs = buildFunctions();fs[0]() //0
fs[1]() //1
fs[2]() //2
console.log (arr), console.log (fs[0]), console.log (fs[1]), console.log (fs[2]),而仔細看f0的scopes中有Closure,並且保存著j:0

--

--

Huang Pei
Huang Pei

Written by Huang Pei

記錄用倉庫,歡迎指正。菜鳥前端,最菜的那種(超能力少女です)。

No responses yet