1. 程式人生 > >js閉包的用途(匿名自執行函式,快取,實現封裝,實現面向物件)

js閉包的用途(匿名自執行函式,快取,實現封裝,實現面向物件)

文章轉載自:http://blog.csdn.net/sunlylorn/article/details/6534610

我們來看看閉包的用途。事實上,通過使用閉包,我們可以做很多事情。比如模擬面向物件的程式碼風格;更優雅,更簡潔的表達出程式碼;在某些方面提升程式碼的執行效率。


1 匿名自執行函式


我們知道所有的變數,如果不加上var關鍵字,則預設的會新增到全域性物件的屬性上去,這樣的臨時變數加入全域性物件有很多壞處,
比如:別的函式可能誤用這些變數;造成全域性物件過於龐大,影響訪問速度(因為變數的取值是需要從原型鏈上遍歷的)。
除了每次使用變數都是用var關鍵字外,我們在實際情況下經常遇到這樣一種情況,即有的函式只需要執行一次,其內部變數無需維護,
比如UI的初始化,那麼我們可以使用閉包:

[javascript] view plaincopyprint?
  1. var datamodel = {    
  2.     table : [],    
  3.     tree : {}    
  4. };    
  5. (function(dm){    
  6.     for(var i = 0; i < dm.table.rows; i++){    
  7.        var row = dm.table.rows[i];    
  8.        for(var j = 0; j < row.cells; i++){    
  9.            drawCell(i, j);    
  10.        }    
  11.     }    
  12.     //build dm.tree    
  13. })(datamodel);   

我們建立了一個匿名的函式,並立即執行它,由於外部無法引用它內部的變數,
因此在執行完後很快就會被釋放,關鍵是這種機制不會汙染全域性物件。

2快取


再來看一個例子,設想我們有一個處理過程很耗時的函式物件,每次呼叫都會花費很長時間,
那麼我們就需要將計算出來的值儲存起來,當呼叫這個函式的時候,首先在快取中查詢,如果找不到,則進行計算,
然後更新快取並返回值,如果找到了,直接返回查詢到的值即可。閉包正是可以做到這一點,因為它不會釋放外部的引用,
從而函式內部的值可以得以保留。

[javascript]
 view plaincopyprint?
  1. var CachedSearchBox = (function(){    
  2.     var cache = {},    
  3.        count = [];    
  4.     return {    
  5.        attachSearchBox : function(dsid){    
  6.            if(dsid in cache){//如果結果在快取中  
  7.               return cache[dsid];//直接返回快取中的物件  
  8.            }    
  9.            var fsb = new uikit.webctrl.SearchBox(dsid);//新建  
  10.            cache[dsid] = fsb;//更新快取  
  11.            if(count.length > 100){//保正快取的大小<=100  
  12.               delete cache[count.shift()];    
  13.            }    
  14.            return fsb;          
  15.        },    
  16.        clearSearchBox : function(dsid){    
  17.            if(dsid in cache){    
  18.               cache[dsid].clearSelection();      
  19.            }    
  20.        }    
  21.     };    
  22. })();    
  23. CachedSearchBox.attachSearchBox("input1");    

這樣,當我們第二次呼叫CachedSearchBox.attachSerachBox(“input1”)的時候,
我們就可以從快取中取道該物件,而不用再去建立一個新的searchbox物件。

3 實現封裝


可以先來看一個關於封裝的例子,在person之外的地方無法訪問其內部的變數,而通過提供閉包的形式來訪問:

[javascript] view plaincopyprint?
  1. var person = function(){    
  2.     //變數作用域為函式內部,外部無法訪問  
  3.     var name = "default";       
  4.     return {    
  5.        getName : function(){    
  6.            return name;    
  7.        },    
  8.        setName : function(newName){    
  9.            name = newName;    
  10.        }    
  11.     }    
  12. }();    
  13. print(person.name);//直接訪問,結果為undefined  
  14. print(person.getName());    
  15. person.setName("abruzzi");    
  16. print(person.getName());    
  17. 得到結果如下:  
  18. undefined  
  19. default
  20. abruzzi  

4 閉包的另一個重要用途是實現面向物件中的物件,傳統的物件語言都提供類的模板機制,
這樣不同的物件(類的例項)擁有獨立的成員及狀態,互不干涉。雖然JavaScript中沒有類這樣的機制,但是通過使用閉包,
我們可以模擬出這樣的機制。還是以上邊的例子來講:

[javascript] view plaincopyprint?
  1. function Person(){    
  2.     var name = "default";       
  3.     return {    
  4.        getName : function(){    
  5.            return name;    
  6.        },    
  7.        setName : function(newName){    
  8.            name = newName;    
  9.        }    
  10.     }    
  11. };    
  12. var john = Person();    
  13. print(john.getName());    
  14. john.setName("john");    
  15. print(john.getName());    
  16. var jack = Person();    
  17. print(jack.getName());    
  18. jack.setName("jack");    
  19. print(jack.getName());    
  20. 執行結果如下:  
  21. default
  22. john  
  23. default
  24. jack  

由此程式碼可知,john和jack都可以稱為是Person這個類的例項,因為這兩個例項對name這個成員的訪問是獨立的,互不影響的。