標籤:代碼 strong 解釋 sof log 返回 asc obj object
一、背景知識
在介紹閉包之前,我覺得有必要先簡單的介紹一些背景知識,如變數的範圍、嵌套函數、記憶體回收機制等概念。
1、範圍
範圍是程式運行時變數可被訪問的範圍,定義在函數內的的變數是局部變數,局部變數的範圍只能是函數內部範圍內,它不能在函數外引用。定義在模組最外層的的變數是全域變數,它是全域範圍內可見的,當然在函數裡面也可以讀取到全域變數的。
var a = 123; //全域變數function fun(){ var b = 456; //局部變數}
2、嵌套函數
函數不僅可以定義在模組的最外層,還可以定義在另外一個函數的內部,像這種定義在函數裡面的函數稱之為“嵌套函數”。如下所示:
1 function foo(){2 function goo() {3 alert(123);4 }5 goo();6 }
3、記憶體回收機制GC
JavaScript具有自動記憶體回收機制(GC:Garbage Collecation),也就是說,執行環境會負責管理代碼執行過程中使用的記憶體。原理:垃圾收集器會定期(周期性)找出那些不在繼續使用的變數,然後釋放其記憶體。
GC在回收記憶體時,首先會判斷該對象是否被其它對象引用。在確定沒有其它對象引用便釋放該對象記憶體地區。
二、什麼是閉包Closure
關於閉包,“官方”的解釋是:閉包是一個擁有許多變數和綁定了這些變數的環境的運算式(通常是一個函數),因而這些變數也是該運算式的一部分。相信很少有人能直接看懂這句話,因為它描述的太學術。
其實,理解閉包的含義並不難。接下來,我將用JavaScript建立一個閉包來協助你理解什麼是閉包,因為作為程式員,有時候,看代碼來理解一個知識點會比你看文字定義來理解更加容易。
function A(){ var foo = 123; function B(){ alert(foo); } return B;}var c = A();c();
上面代碼翻譯成自然語言如下:
(1)定義了一個普通函數A
(2)在A中定義了普通函數B
(3)在A中返回B(確切的講,在A中返回B的引用)
(4)執行A(),並把A的傳回值賦給變數 c
(5)執行 c()
把上面這五個步驟總結一下就是:函數A的內建函式B被函數A外的一個變數 c引用;
把這句再加工一下就變成了閉包的定義:當一個內建函式被其外部函數之外的變數引用時,就形成了一個閉包。
也可以這樣說:閉包是指有權訪問另一個函數範圍中的變數的函數。
三、閉包的作用
其實,閉包與普通函數的差別在於局部變數可以在函數執行結束後仍然被函數外的代碼訪問。這意味著函數必須返回一個指向閉包的“引用”,或將這個“引用”賦值給某個外部變數,才能保證閉包中局部變數被外部代碼訪問。當然包含這個引用的實體應該是一個對象,因為在Javascript中除了基本類型剩下的就都是對象了。
簡而言之,閉包的作用就是在A執行完並返回後,閉包使得Javascript的記憶體回收機制GC不會收回A所佔用的資源,因為A的內建函式B的執行需要依賴A中的變數。
所以,當我們需要在一個模組中定義一個能一直儲存在記憶體中但又不會“汙染”全域的變數的時候,我們就可以用閉包來定義這個模組。
四、閉包的應用情境
1、保護函數內的變數安全。以上面第三個例子為例,函數A中foo只有函數B才能訪問,而無法通過其他途徑訪問到,因此保護了f的安全性。
2、在記憶體中維持一個變數。依然如前例,由於閉包,函數A中foo的一直存在於記憶體中,因此每次執行c(),都會彈出內容為“123”的彈出框。
以上兩點是閉包最基本的應用情境,很多經典案例都源於此。
五、閉包為什麼不會被JS記憶體回收機制回收
在JavaScript中,如果一個對象不再被引用,那麼這個對象就會被記憶體回收機制GC回收。如果兩個對象互相引用,而不再被第3者所引用,那麼這兩個互相引用的對象也會被回收。
以上面第三個例子為例:因為函數A被函數B引用,函數B又被函數A外的變數c引用,這也就是為什麼函數A執行後不會被回收的原因。
六、總結
以上就是對閉包簡單的理解,其實,關於閉包的知識點還遠不止上述所講到的這些,要想更深入的去瞭解閉包,就回涉及到JS的執行環境(execution context)、使用中的物件(activation object)以及範圍(scope)和範圍鏈(scope chain)的運行機制等這些概念。但是,作為一位初學者,其實暫時可以不必瞭解這些的。
當然,如果你還想對閉包有一個進一步的瞭解,可以去看一下我的另一篇部落格“深入理解JavaScript閉包”。
參考資料:http://blog.csdn.net/hitman9099/article/details/3854171
淺談JavaScript閉包