亂談閉包

conqorld發表於2019-04-16

關於閉包,有各種各樣的解釋,百科給出的解釋是:

在電腦科學中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函式閉包(function closures),是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。閉包在執行時可以有多個例項,不同的引用環境和相同的函式組合可以產生不同的例項。

我的理解是:函式執行形成私有的作用域。 那麼他就有兩個作用:保護和儲存。

閉包的保護作用

閉包形成的私有作用域可以保護裡面的私有變數不受外界干擾。例如我們經常使用的:

var a = 5;
(function () {
    var a = 10;
    console.log(a)   //  ---->10
})()
console.log(a)   //  ---->5
複製程式碼

這時在全域性中有個a變數,私有作用域中也有個a變數,使用閉包,我們再修改私有作用域中的同名變數是,不會對全域性產生影響,避免造成全域性汙染。

閉包的儲存作用

形成一個不銷燬的私有作用域,保護函式執行的上下文。例如之前我們寫函式的防抖和節流的時候:

function debounce(fn, time) {
    let Timer;
    return () => {
        Timer && clearTimeout(Timer)
        Timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, time)
    }
}
複製程式碼

debounce函式儲存了一個計時器id的變數Timer,debounce執行 形成一個可以讓返回函式訪問到的私有作用域(函式的上級作用域是它定義時所處的作用域),每次呼叫返回函式的時候都可以訪問得到這個Timer。

閉包的副作用

舉個老是被拿來舉的閉包例子:

var liList = document.querySelectorAll('li')
for (var i = 0; i < liList.length; i++) {
    (function (i) {
        liList[i].onclick = function () {
            console.log(i)
        }
    })(i)
}
複製程式碼

由於返回的函式被外部的onclick事件引用了,閉包形成的私有作用域無法被銷燬,記憶體無法被回收,回收機制參考js垃圾回收機制,容易造成記憶體洩漏。

若要手動釋放記憶體,則那麼寫:

for (var i = 0; i < liList.length; i++) {
    liList[i].onclick = null
}
複製程式碼

並不是所有閉包都會造成記憶體無法回收,例如:

function fn(i) {
    return function (n){
        console.log(i + n)
    }
}

fn(2)(3)
複製程式碼

fn形成的私有作用於中,變數並沒有被外界所引用,函式執行完成後,記憶體立刻進行回收。

相關文章