對閉包的一點理解

什么是閉包

我理解的就是在一個作用域中可以訪問另一個作用域的變量,這種現(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容