本文基於《JavaScript高級程序設計》整理。
閉包概念:
閉包是一類函數。哪一類?有權訪問 另一個 函數 作用域中變量的函數。
想要理解閉包,必須從理解函數被【調用】的時候會發生什麼入手。
每次複習閉包的相關知識,我都習慣把作用域和閉包一起復習。
函數的作用域鏈
1.創建函數outerFun()時,會創建一個預先包含全局變量對象的作用域鏈,保存在內部的[[Scope]]屬性中。
2.調用函數outerFun()時,爲此函數創建一個執行環境。
3.然後複製函數的[[Scope]]屬性中的對象構建起執行環境的作用域鏈。
4.此後,創建一個活動對象,推入執行環境作用域鏈的前端([0]位置)。
此時執行環境的作用域鏈中包含兩個變量對象:全局變量對象(第3步) 、 局部活動對象(第4步)。
函數中訪問一個變量時,就會從作用域鏈中搜索具有相應名字的變量。
函數執行完後,局部活動對象被銷燬,內存中僅保存全局作用域。
閉包的情況
🌟內部函數 被 外部函數 包含時,內部函數會將外部函數的局部活動對象添加到自己的作用域鏈中。
outerFun(outerArgument){
//被包含的內部函數可以訪問外部函數的變量
return function(){
return outerArgument+1
}
}
而由於內部匿名函數的作用域鏈 在引用 外部包含函數的活動對象 ,即使outFun執行完畢了,它的活動對象還是不會被銷燬!
即,outerFun的執行環境作用域鏈都銷燬了,它的活動對象還在內存中留着呢。
並且根據垃圾回收機制,被另一個作用域引用的變量不會被回收。
所以,除非內部的匿名函數解除對活動變量的引用(解除對匿名函數的引用),纔可以釋放內存。
// 創建函數 還未調用
var creatFun = outerFun(7)
// 調用函數
var result = creatFun(8)
// 解除對匿名函數的引用
creatFun = null
所以,閉包會造成內存泄漏,就是因爲它把外部的包含它的函數的活動對象也包含在自己的作用域鏈中了,會比一般的函數佔用更多內存。
如何減少閉包占用的內存問題?見下一篇:閉包如何減少內存佔用?