Javascript之旅——第十站:為什麼都說閉包難理解呢?

發表於2015-01-28

研究過js的朋友大多會說,理解了js的原型和閉包就可以了,然後又說這些都是js的高階內容,然後就又扯到了各種神馬的作用域。。。然後不少人就會被忽悠的雲裡霧裡。。。下面我也試著來說說閉包,看我說的這個是否淺顯易懂。。。

一:閉包含義

閉包是個專業詞彙,這樣才能顯得在js中是高大上的貨色,官方定義我這裡就不敢修改它,定義如下:就是有權訪問另一個函式作用域的變數的函式。

二:一個簡單的場景

上面的定義大概也能看得懂,但是不知道為什麼不把“另一個函式” 改成 “包含函式”,因為我覺得“包含函式”可能更通俗易懂些,光有定義還不行,我還得找個經典的例子看一看。

這是一個說閉包原理的經典例子,經典在哪裡?如例子中我使用compare時,我的function是可以訪問到createComparison函式中的propertyName欄位的,其實這個理解並不複雜,我們去看看瀏覽器的scope variables就一清二楚了。

我們可以清楚的看到,在chrome的本地變數表中清楚的記錄著當前執行函式中的本地變數列表,並且還進行了分類,比如上面的”區域性函式變數(Local)“,”包含函式變數(Closure)”,“全域性變數(Global)”,那下面有個有趣的問題就來了,chrome怎麼知道我程式碼執行到20行的時候,當前的local variables有哪些呢?而且還能給我分門別類,是不是太奇葩了????但是仔細推敲一下就能豁然開朗,肯定有一個變數儲存著當前的variables,不然的話,chrome去哪讀取呢?對不對????????

三:解開謎底

其實在每個function裡面都有一個scope屬性,當然這個屬性被引擎遮蔽了,你是看不見也摸不著的,它裡面就儲存著當前函式的 local variables,如果應用到上面demo的話,就是全域性函式中有一個scope,createComparison有一個scope,匿名的compare有一個scope,而且這三個scope還是通過連結串列連結的,畫個簡圖如下:

從上面簡圖中可以看到,其實整個函式中有三個scope,每個scope都是用next指標連結,這樣就形成了一個連結串列,當我執行下面程式碼的時候

js引擎會拿到當前compare的scope,通過scope屬性的next指標,就可以區分哪些變數屬於哪個函式,這樣你就看到了chrome對variables的分門別類了。

四:對一個案例的加深理解

我想讀到這裡,你應該明白了閉包的原理,其實沒什麼稀奇的,就是一個讀取scope屬性的問題。只是被裝逼成高大上了,下面看段程式碼:

在這個例子中,我想做一個function()陣列的array,並且最後都能輸出各自的值(1,2,3,4,5…10),但是結果又是怎樣呢?可以看到下圖中輸出的其實是10個10。。。這樣就違背了我的原始意圖。

上面這個陷阱的最大問題在於你自以為我在匿名function中寫了return i;就認為它是屬於匿名函式的,其實這就大錯特錯了,因為這個i就算走到天涯海角都不屬於匿名函式,而是屬於它的包含函式Person,所以原理應該是這樣,比如你看,當我執行arr[0]()的時候,這時候匿名函式就會通過scope去找i,但是在匿名函式的scope中沒有i,所以就通過next找到了Person函式,確實在Person中找到了i,但是這個時候i已經是10了,然後結束scope查詢輸出10。解決方案也很簡單,給每個匿名function一個副本就好了,具體原理我想你應該可以用scope推測出來了,對不對。

相關文章