[JS]什麼是閉包?

shiramashiro發表於2021-11-13

首先來思考一下下面的案例:

function unclosure() {
    let count = 0
    return count++
}

for (let index = 0; index < 10; index++) {
    let result = unclosure()
    console.log(result)
}

我在函式內實現一個很簡單的計數器功能,希望在最後得到一個成功累加之後的數字。unclosure 函式是沒有利用閉包寫的計數器。

通過 for 的10次迴圈,列印 result,當我們開啟瀏覽器執行這一段程式碼之後:

[JS]什麼是閉包?

unclosure 函式被重複呼叫了10次,每次的 count 變數都不會成為下一次函式計數時使用的那個 count 變數。也就是說,每次函式重新執行的時候,此 count 變數非上一次 count 變數,所以,對於下一次函式執行時面對的 count 變數初始值為 0。那麼,應該如何解決這個問題呢?

一般會想到把 count 變數提到 unclosure 函式體之外,這樣做是肯定可以的。實際上,這樣的操作已經是在利用閉包了。讓我們改造上面的案例:

let count = 0
function unclosure() {
    return count++
}

一個函式體內引用了外部的變數時,函式就會形成一個閉包。直到這個函式結束執行,閉包也會隨之消失。閉包形成一個持續的環境,閉包內的所有資源都將持續存在著,所以,上面的例子會成功進行累加。

如果不把變數提取到函式體外,直接就在函式裡面執行也是可以的,應該如何製造一個閉包呢?

函式引用了外部的變數時,就會形成一個閉包。一句這一結論,我們真的可以在第一個案例中製造一個閉包,來解決最先出現的問題。

function closure() {
    let count = 0
    console.log('closure 函式初始化了一次')
    return function () {
        return count++
    }
}

let inner = closure() // 得到一個閉包

for (let index = 0; index < 10; index++) {
    let result = inner()
    console.log(result)
}

closure 函式的內部巢狀了一個匿名函式(也可以巢狀有名函式)。相對於匿名函式來說,它引用了外部函式 closure 的變數 count,這就形成了一個閉包了。

在執行這段程式碼之前,我們需要得到 closure 函式的閉包(不要重複得到一個新的閉包,因為會初始化這個環境,回到上訴提到的問題)。最後反覆執行 inner() 就可以實現累加了。

[JS]什麼是閉包?

相關文章