閉包是有權訪問另外一個函數做用域中的變量的函數。javascript
以下代碼:根據變量做用域,函數outer中全部的局部變量對函數inner都是可見的。可是反過來不行,inner內部的局部變量對outer是不可見的。這就是JavaScript語言特有的鏈式做用域結構(chain scope),子對象會一級一級向上尋找全部父對象的變量。因此父對象的全部變量對子對象都是可見的,反之則不成立。html
既然函數inner能讀取函數outer的局部變量,那麼只要將inner做爲返回值,就能夠直接在outer外部讀取它內部的變量。java
這個函數有兩個特色:數組
1)函數inner嵌套在函數outer內部閉包
2)函數outer返回函數inner函數
執行完var rs = outer()後,實際rs指向函數inner。這段代碼其實就是一個閉包。也就是說當函數outer內的函數inner函數被函數outer外的一個變量引用的時候,就建立了一個閉包。this
上面的代碼中,rs是閉包inner函數。rs共運行了3次,第一次100,第二次101,第三次102,這說明函數outer中的局部變量i一直保存在內存中,並無在調用後清除。spa
閉包的做用:在outer執行完畢並返回後,閉包是JavaScript的垃圾回收機制(garbage collection)不會回收outer所佔的內存,由於outer函數內部的inner函數執行要依賴outer中的變量。(另外一種解釋:outer是inner的父函數,inner被賦給了一個全局變量,致使inner會一直在內存中,而inner的存在依賴於outer,所以outer也一直存在於內存中,不會在調用結束後被垃圾回收機制(garbage collection)回收。) code
由上述可得知:htm
1)閉包有權訪問函數內部的全部變量;
2)當函數返回一個閉包時,這個函數的做用域會一直在內存中保存直到閉包不存在爲止。
做用鏈機制,閉包容許內層函數引用父函數中的變量,但該變量是最終值。
上述代碼執行後,fn爲一個數組,表面上看,每一個函數執行後應該返回本身的索引值,但實際上,每一個函數都返回10。fn數組中每一個函數的做用域鏈上都保存着函數f的活動對象,它們引用的是同一變量i。當函數f返回後,i的值爲10。
強制建立另外一個閉包讓函數的行爲符合預期,以下代碼所示。
這個版本中,沒有直接將閉包賦值給數組,而是定義了一個匿名函數,並將當即執行匿名函數的結果賦值給數組。這裏的匿名函數有一個參數num,在調用每一個函數時,傳入變量i,因爲參數是按值傳遞的,所以將變量i複製給參數num。而在這個匿名函數內部又建立並返回了一個訪問num的閉包,所以rs數組中每一個函數包含本身的num變量,所以就能夠返回不一樣的數值。
以下代碼:建立了做爲el元素事件處理程序的閉包,而這個閉包又建立了循環引用,只要匿名函數存在,el的引用數至少爲1,所以它所佔用的內存就永遠不會被回收。
以下代碼:把變量el設置爲null,可以解除對DOM對象的引用,確保正常回收其佔用內存。
任何一對花括號中的語句集都屬於一個塊,在這之中定義的全部變量在代碼塊外都是不可見的,稱之爲塊級做用域。
1)讀取函數內部的變量
如上述例子中, 函數inner可以訪問函數outer中的變量,將函數inner做爲返回值,就能直接在outer外部讀取它的變量。
2)在內存中維持變量
如上述例子中,因爲閉包,函數outer一直存在於內存中,所以每次執行rs(),都會給i加1。
時間:2014-10-23
地點:合肥