深入理解JavaScript閉包概念__Java

來源:互聯網
上載者:User
閉包向來給包括JavaScript程式員在內的程式員以神秘,高深的感覺,事實上,閉包的概念在函數式程式設計語言中算不上是難以理解的知識。如果對範圍,函數為獨立的對象這樣的基本概念理解較好的話,理解閉包的概念並在實際的編程實踐中應用則頗有水到渠成之感。
 
在DOM的事件處理方面,大多數程式員甚至自己已經在使用閉包了而不自知,在這種情況下,對於瀏覽器中內嵌的JavaScript引擎的bug可能造成記憶體流失這一問題姑且不論,就是程式員自己調試也常常會一頭霧水。
 
用簡單的語句來描述JavaScript中的閉包的概念:由於JavaScript中,函數是對象,對象是屬性的集合,而屬性的值又可以是對象,則在函數內定義函數成為理所當然,如果在函數func內部聲明函數inner,然後在函數外部調用inner,這個過程即產生了一個閉包。
 
閉包的特性
我們先來看一個例子,如果不瞭解JavaScript的特性,很難找到原因:
var outter = [];  
function clouseTest () {  
    var array = ["one", "two", "three", "four"];  
    for(var i = 0; i < array.length;i++){  
       var x = {};  
       x.no = i;  
       x.text = array[i];  
       x.invoke = function(){  
           print(i);  
       }  
       outter.push(x);  
    }  
}  
   
//調用這個函數  
clouseTest();  
   
print(outter[0].invoke());  
print(outter[1].invoke());  
print(outter[2].invoke());  
print(outter[3].invoke());
啟動並執行結果如何呢。很多初學者可能會得出這樣的答案:
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。
 
如何解決這一問題呢。我們可以聲明一個匿名函數,並立即執行它:
var outter = [];  
   
function clouseTest2(){  
    var array = ["one", "two", "three", "four"];  
    for(var i = 0; i < array.length;i++){  
       var x = {};  
       x.no = i;  
       x.text = array[i];  
       x.invoke = function(no){  
           return function(){  
              print(no);  
           }  
       }(i);  
       outter.push(x);  
    }    
}  
   
clouseTest2();  
 
這個例子中,我們為x.invoke賦值的時候,先運行一個可以返回一個函數的函數,然後立即執行之,這樣,x.invoke的每一次迭代器時相當與執行這樣的語句:
//x == 0  
x.invoke = function(){print(0);}  
//x == 1  
x.invoke = function(){print(1);}  
//x == 2  
x.invoke = function(){print(2);}  
//x == 3  
x.invoke = function(){print(3);}  
這樣就可以得到正確結果了。閉包允許你引用存在於外部函數中的變數。然而,它並不是使用該變數建立時的值,相反,它使用外部函數中該變數最後的值。
 
閉包的用途
現在,閉包的概念已經清晰了,我們來看看閉包的用途。事實上,通過使用閉包,我們可以做很多事情。比如類比物件導向的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提升代碼的執行效率。
 
匿名自執行函數
上一節中的例子,事實上就是閉包的一種用途,根據前面講到的內容可知,所有的變數,如果不加上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);  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.