咱們來看看閉包的用途。事實上,經過使用閉包,咱們能夠作不少事情。好比模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提高代碼的執行效率。web
1 匿名自執行函數緩存
咱們知道全部的變量,若是不加上var關鍵字,則默認的會添加到全局對象的屬性上去,這樣的臨時變量加入全局對象有不少壞處,
好比:別的函數可能誤用這些變量;形成全局對象過於龐大,影響訪問速度(由於變量的取值是須要從原型鏈上遍歷的)。
除了每次使用變量都是用var關鍵字外,咱們在實際狀況下常常遇到這樣一種狀況,即有的函數只須要執行一次,其內部變量無需維護,
好比UI的初始化,那麼咱們能夠使用閉包:閉包
var datamodel = { table : [], tree : {} }; (function(dm){ for(var i = 0; i < dm.table.rows; i++){ var row = dm.table.rows[i]; for(var j = 0; j < row.cells; i++){ drawCell(i, j); } } //build dm.tree })(datamodel);
咱們建立了一個匿名的函數,並當即執行它,因爲外部沒法引用它內部的變量,
所以在執行完後很快就會被釋放,關鍵是這種機制不會污染全局對象。
2緩存函數
再來看一個例子,設想咱們有一個處理過程很耗時的函數對象,每次調用都會花費很長時間,
那麼咱們就須要將計算出來的值存儲起來,當調用這個函數的時候,首先在緩存中查找,若是找不到,則進行計算,
而後更新緩存並返回值,若是找到了,直接返回查找到的值便可。閉包正是能夠作到這一點,由於它不會釋放外部的引用,
從而函數內部的值能夠得以保留。ui
var CachedSearchBox = (function(){ var cache = {}, count = []; return { attachSearchBox : function(dsid){ if(dsid in cache){//若是結果在緩存中 return cache[dsid];//直接返回緩存中的對象 } var fsb = new uikit.webctrl.SearchBox(dsid);//新建 cache[dsid] = fsb;//更新緩存 if(count.length > 100){//保正緩存的大小<=100 delete cache[count.shift()]; } return fsb; }, clearSearchBox : function(dsid){ if(dsid in cache){ cache[dsid].clearSelection(); } } }; })(); CachedSearchBox.attachSearchBox("input1");
這樣,當咱們第二次調用CachedSearchBox.attachSerachBox(「input1」)的時候,
咱們就能夠從緩存中取道該對象,而不用再去建立一個新的searchbox對象。
3 實現封裝spa
能夠先來看一個關於封裝的例子,在person以外的地方沒法訪問其內部的變量,而經過提供閉包的形式來訪問:code
var person = function(){ //變量做用域爲函數內部,外部沒法訪問 var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }(); print(person.name);//直接訪問,結果爲undefined print(person.getName()); person.setName("abruzzi"); print(person.getName()); 獲得結果以下: undefined default abruzzi
4 閉包的另外一個重要用途是實現面向對象中的對象,傳統的對象語言都提供類的模板機制,
這樣不一樣的對象(類的實例)擁有獨立的成員及狀態,互不干涉。雖然JavaScript中沒有類這樣的機制,可是經過使用閉包,
咱們能夠模擬出這樣的機制。仍是以上邊的例子來說:orm
function Person(){ var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }; var john = Person(); print(john.getName()); john.setName("john"); print(john.getName()); var jack = Person(); print(jack.getName()); jack.setName("jack"); print(jack.getName()); 運行結果以下: default john default jack
由此代碼可知,john和jack均可以稱爲是Person這個類的實例,由於這兩個實例對name這個成員的訪問是獨立的,互不影響的。對象