如果對作用域,函數為獨立的對象這樣的基本概念理解較好的話,理解閉包的概念並在實際的編程實踐中應用則頗有水到渠成之感。
在DOM的事件處理方面,大多數程序員甚至自己已經在使用閉包了而不自知,在這種情況下,對於浏覽器中內嵌的JavaScript引擎的bug可能造成內存洩漏這一問題姑且不論,就是程序員自己調試也常常會一頭霧水。
用 簡單的語句來描述JavaScript中的閉包的概念:由於JavaScript中,函數是對象,對象是屬性的集合,而屬性的值又可以是對象,則在函數內 定義函數成為理所當然,如果在函數func內部聲明函數inner,然後在函數外部調用inner,這個過程即產生了一個閉包。
閉包的特性:
我們先來看一個例子,如果不了解JavaScript的特性,很難找到原因:
運行的結果如何呢?很多初學者可能會得出這樣的答案:
0
1
2
3
然而,運行這個程序,得到的結果為:
4
4
4
4
其 實,在每次迭代的時候,這樣的語句x.invoke = function(){print(i);}並沒有被執行,只是構建了一個函數體為”print(i);”的函數對象,如此而已。而當i=4時,迭代停 止,外部函數返回,當再去調用outter[0].invoke()時,i的值依舊為4,因此outter數組中的每一個元素的invoke都返回i的 值:4。如何解決這一問題呢?我們可以聲明一個匿名函數,並立即執行它:
這個例子中,我們為x.invoke賦值的時候,先運行一個可以返回一個函數的函數,然後立即執行之,這樣,x.invoke的每一次迭代器時相當與執行這樣的語句:
這樣就可以得到正確結果了。閉包允許你引用存在於外部函數中的變量。然而,它並不是使用該變量創建時的值,相反,它使用外部函數中該變量最後的值。
閉包的用途:
現在,閉包的概念已經清晰了,我們來看看閉包的用途。事實上,通過使用閉包,我們可以做很多事情。比如模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提升代碼的執行效率。
緩存:
再來看一個例子,設想我們有一個處理過程很耗時的函數對象,每次調用都會花費很長時間,那麼我們就需要將計算出來的值存儲起來,當調用這個函數的時候,首先在緩存中查找,如果找不到,則進行計算,然後更新緩存並返回值,如果找到了,直接返回查找到的值即可。
閉包正是可以做到這一點,因為它不會釋放外部的引用,從而函數內部的值可以得以保留。
實現封裝:
得到結果如下:
undefined
default
jack
閉包的另一個重要用途是實現面向對象中的對象,傳統的對象語言都提供類的模板機制,這樣不同的對象(類的實例)擁有獨立的成員及狀態,互不干涉。雖然JavaScript中沒有類這樣的機制,但是通過使用閉包,我們可以模擬出這樣的機制。還是以上邊的例子來講:
復制代碼 代碼如下:
運行結果如下:
default
john
default
jack
javascript閉包應該注意的問題:
1.內存洩漏:
在 不同的JavaScript解釋器實現中,由於解釋器本身的缺陷,使用閉包可能造成內存洩漏,內存洩漏是比較嚴重的問題,會嚴重影響浏覽器的響應速度,降 低用戶體驗,甚至會造成浏覽器無響應等現象。JavaScript的解釋器都具備垃圾回收機制,一般采用的是引用計數的形式,如果一個對象的引用計數為 零,則垃圾回收機制會將其回收,這個過程是自動的。但是,有了閉包的概念之後,這個過程就變得復雜起來了,在閉包中,因為局部的變量可能在將來的某些時刻 需要被使用,因此垃圾回收機制不會處理這些被外部引用到的局部變量,而如果出現循環引用,即對象A引用B,B引用C,而C又引用到A,這樣的情況使得垃圾 回收機制得出其引用計數不為零的結論,從而造成內存洩漏。
2.上下文的引用:
此處的alert(this.id)到底引用著什麼值呢?很多開發者可能會根據閉包的概念,做出錯誤的判斷:
content
理 由是,this.id顯示的被賦值為content,而在click回調中,形成的閉包會引用到this.id,因此返回值為content。然而事實 上,這個alert會彈出”panel”,究其原因,就是此處的this,雖然閉包可以引用局部變量,但是涉及到this的時候,情況就有些微妙了,因為 調用對象的存在,使得當閉包被調用時(當這個panel的click事件發生時),此處的this引用的是con這個jQuery對象。而匿名函數中的 this.id = “content”是對匿名函數本身做的操作。兩個this引用的並非同一個對象。
如果想要在事件處理函數中訪問這個值,我們必須做一些改變:
這樣,我們在事件處理函數中保存的是外部的一個局部變量self的引用,而並非this。這種技巧在實際應用中多有應用,我們在後邊的章節裡進行詳細討論。關於閉包的更多內容,我們將在第九章詳細討論,包括討論其他命令式語言中的“閉包”,閉包在實際項目中的應用等等。
附:由於本身水平有限,文中難免有纰漏錯誤等,或者語言本身有不妥當之處,歡迎及時指正,提出建議。本文只為拋磚引玉,謝謝大家!