在上一次的筆記中([筆記] 談談JavaScript中的closure的概念),我們說明了closure的概念,這篇筆記我們則會看更進一步的例子。
讓我們先來看一下這段程式碼:
我們先建立一個函式,名稱為buildFunctions,接著在函式裡面建立陣列arr,然後我們建立一個迴圈,迴圈每執行一次,就會把一個function放入陣列中,函式的最後,則是回傳arr這個陣列。
程式的最後,我們利用 fs[0]( ) 來執行每個陣列中的函式,猜猜看結果會得到什麼呢?
你覺得會是0, 1, 2嗎?
答案是3, 3, 3。為什麼呢?
讓我們來進一步了解這個程式執行的過程發生了什麼事?
首先,在程式碼執行的開始,buildFunctions和變數fs會由於hoisting的緣故,先被建立在記憶體中,接著在開始逐行執行程式,這時候的Global Execution Context如下:
接著會執行到 var fs = buildFunctions( ) ,於是產生了bulidFunctions的execution context,在裡面的迴圈執行完之後,會產生一個變數 i 和陣列 arr。
當我們執行到函式中的 return arr 時,變數 i 和陣列arr的值會是什麼呢?
我們來思考一下,當buildFunctions這個函式開始執行到for迴圈時,一開始 i = 0,這時候會把陣列的內容 function( ){console.log(i)} 儲存到陣列中,但要注意的是這時候這個被儲到陣列中的函式並沒有執行(invoke),而是只是儲存在裡面而已,因為它沒有透過括號 ( ) 來執行;然後 i 會繼續累加,當 i 累加到3的時候,因為不符合 i < 3 所以會跳出迴圈。這也就是為什麼 i = 3。
當我們透過 console.log( fs[0] ) 來看陣列的內容,同樣地會發現三個陣列的內容都是一樣的function:
當bulidFunctions這個函式執行結束後,便會抽離execution stack,而buildFunctions的execution context也就消失了。但由上一堂課我們談到的closure的概念可以知道,雖然execution context消失了,但是這個execution context內所儲存的變項仍然留有參照,仍然可以透過Scope Chain找到它們。
執行完之後,fs[0]( )的execution context就結束了,再次從execution stack中抽離。以此類推,繼續進行fs[1]( )和fs[2]( )的execution context,它們和fs[0]( )的execution context有同樣的outer environment reference,因為這個function一開始建立的時候,是在同一個位置。
看看另一個範例
讓我們在看一個和closure有關的例子,看看你是不是對closure的概念更了解了:
我們要知道,當我們在執行buildFunctions2的時候,其實並還沒有執行裡面number這個function,number這個function只是被儲存在記憶體中而已。雖然bulidFunctions2已經先執行完,離開execution stack了,但execution context裡面的 i 仍然被存在記憶體中,因此,當我後來在執行 output 的時候,仍然可以透過scope chain找到 i 這個變數的值。
範例程式
function buildFunctions(){ var arr = []; for(var i = 0; i < 3; i++){ arr.push( function(){ console.log(i); } ) } return arr; } var fs = buildFunctions(); fs[0](); fs[1](); fs[2](); /*-------------------------*/ function buildFunctions2(){ i = 3; function number(){ console.log(i); } return number; } var output = buildFunctions2(); console.log(output); output();→回到此系列文章目錄
0 意見:
張貼留言