什么是閉包
我理解的就是在一個作用域中可以訪問另一個作用域的變量,這種現(xiàn)象叫做閉包。
最簡單的閉包形式就是在一個函數(shù)內(nèi)部嵌套一個函數(shù),嵌套的函數(shù)里又使用了外部函數(shù)的局部變量,最后再返回嵌套的函數(shù),如下:
function fn () {
let n = 1;
function add () {
n++;
}
return add;
}
閉包一定需要return嗎
平常見到的閉包大多都長這樣,好像都會在函數(shù)內(nèi)部return個啥,但是是不是就一定需要return呢,這樣行不行呢?
function fn () {
let n = 1;
function add () {
n++;
}
window.add = add;
}
在調(diào)用fn()之后,全局都可以調(diào)用window.add()了,也就可以全局訪問fn()的局部變量n了,這應(yīng)該也是閉包吧。所以閉包跟return是沒有啥關(guān)系的,不一定需要return。
為啥會形成閉包
對于變量都有一個作用域的概念,作用域控制著變量的可見性和生命周期。作用域又分為全局作用域和局部作用域,全局作用域指在代碼的任何地方都能訪問,局部作用域常見的就是函數(shù)作用域,在函數(shù)外部無法訪問函數(shù)內(nèi)部的變量,但根據(jù)作用域鏈,函數(shù)內(nèi)部又可以訪問函數(shù)外部的變量。當函數(shù)內(nèi)部的變量被函數(shù)內(nèi)部嵌套的函數(shù)使用,同時這個嵌套函數(shù)被賦值給了一個全局變量時,那么函數(shù)內(nèi)部的值又可以通過這個全局變量在全局訪問到,這樣就形成了閉包。
閉包的作用
雖說閉包能夠讓全局都能訪問到某個函數(shù)的局部變量,但也并不代表隨便啥刀槍棍棒的都能訪問修改這個變量,而是指定了訪問的路徑和方法,必須通過規(guī)定的方式才能去訪問和修改這個變量。
通過對比正常定義的全局變量,正常定義的全局變量隨隨便便都可以訪問修改,而閉包就好像是將一個全局變量給隱藏了起來,既是全局的,又是私有的,只是暴露一個訪問器,全局通過這個訪問器訪問。
// 正常定義一個全局變量
var a = 1;
console.log(a); // 這里可以訪問a
a = 2; // 這里可以修改a
function addOfA () {
a++; // 這里還可以修改a
}
addOfA(); // => a = 3
// 通過閉包的形式給一個全局可以訪問的變量
(function fn () {
let n = 1;
function add () { // 只能通過這個方法修改和訪問n
n++;
console.log(n)
}
window.add = add
})();
add(); // => n = 2
console.log(n); // => 報錯:Uncaught ReferenceError: n is not defined
所以閉包的作用主要有:
- 函數(shù)外讀取函數(shù)局部變量;
- 閉包父級做用域變量的值不會由于函數(shù)的執(zhí)行完畢而銷毀,變量保存的值可以保存下來;
- 模塊化的公有屬性和方法暴露出來;
閉包關(guān)于內(nèi)存泄漏的問題
先百度一下啥叫內(nèi)存泄漏:內(nèi)存泄漏(Memory Leak)是指程序中已動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。
這在js中也就是引用數(shù)據(jù)類型沒有及時釋放引用,垃圾回收機制無法回收這些變量,造成性能的浪費。js中內(nèi)存管理的主要概念是可達性,也就是只要這個變量還可以被訪問,那就不會回收這個變量的內(nèi)存,以保證它隨時可以被訪問。全局作用域的變量一般不會被回收,因為不知道啥時候會使用它。而局部變量在所在作用域中執(zhí)行完畢后會被銷毀,因為后面就不會再需要這個變量。
但是在閉包中又有所不同,閉包中的變量隨時局部變量,在所在閉包函數(shù)運行結(jié)束后依然有路徑可以訪問到閉包函數(shù)內(nèi)部的變量,也就滿足了js內(nèi)存管理的可達性,這個變量就不會被回收。當然在我們的代碼還需要使用這個變量時應(yīng)該算不上是內(nèi)存泄漏,只有在不需要再使用它時,我們需要通過某種方式告訴js的垃圾回收機制,這個變量可以被銷毀了,否則就會造成內(nèi)存泄漏的問題。
那么如何告訴垃圾回收機制這個變量該回收了呢?
var closure = (function() {
var n = 0;
return {
add: function () {
return ++n;
},
clearVariable: function () {
n = null;
}
}
})();
在閉包內(nèi)部返回一個刪除閉包的方法, 該方法將閉包內(nèi)部的變量設(shè)置為null, 讓變量失去引用,這樣變量會被系統(tǒng)自動回收。
對于閉包的變量回收還有些更細節(jié)的問題,比如變量回收在不同瀏覽器內(nèi)核上的表現(xiàn)差異等,這些可以參考這位大神的文章。https://www.cnblogs.com/rubylouvre/p/3345294.html