關於JavaScript的閉包

1、前言

閉包它在百度百科中的解釋原話是這樣的:閉包就是可以讀取其餘函數內部變量的函數。例如在javascript中,只有函數內部的子函數才能讀取局部變量,因此閉包能夠理解成定義在一個函數內部的函數。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。javascript

下面來一一解讀這段話。java

2、閉包是什麼?

首先來直接回答這個問題:閉包是什麼?閉包

答案:閉包是一個函數,什麼函數呢?一個定義在函數內部的函數。例如:異步

var add = (function () {
    var counter = 0;
    return function () {
        return counter += 1;
    }
})();
複製代碼

在上面的例子中,函數內部return的這個函數function () {return counter += 1;},它就是一個閉包。咱們直接把add在控制檯打印出來: 函數

咱們能夠看到,在這裏全局定義的這個變量 add,它其實是一個函數,可是這個函數倒是在另外一個函數內部建立的,這個就是 定義在函數內部的函數

3、閉包有什麼做用?

一、閉包能夠訪問到局部變量

即咱們要解讀這句話:可以讀取其餘函數內部變量的函數性能

咱們知道,在JavaScript中,有全局變量和局部變量。顧名思義,全局變量它是公有的、共享的,程序內的全部函數均可以直接調用這個全局變量,即它的做用域是全局性的;而局部變量,在函數內部聲明的變量,它是私有的,只在函數內部起做用,在該函數以外的地方是沒法被調用的,它的做用域是局部性的。ui

function add() {
    var counter = 0;
    return counter += 1;
}
console.log(add()); // 1
console.log(add()); // 1
console.log(add()); // 1
// 本意是想輸出 3, 但事與願違,輸出的都是 1 
複製代碼

以上代碼將沒法正確輸出,每次調用add()函數,計數器都會設置爲1。能夠將它寫成閉包的形式,使counter = 0只執行一次,並返回函數表達式,而後獲得咱們想要輸出的結果。spa

function add() {
    var counter = 0;
    return function () {
        return counter += 1;
    }
}
var closure = add();
console.log(closure()); // 1
console.log(closure()); // 2
console.log(closure()); // 3
複製代碼

閉包它能夠訪問函數上一層做用域的局部變量,它使得函數擁有私有變量變成可能。3d

二、閉包能夠保護裏面的局部變量,使它們不會隨着函數的結束而銷燬

百度百科裏面還有這麼一段話:「閉包」一詞來源於如下二者的結合:要執行的代碼塊(因爲自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和爲自由變量提供綁定的計算環境(做用域)。code

這句話含有一個意思,就是在閉包內部的變量它是不會被銷燬的,會一直存在內存中,而且不會受到外界的干擾。在上面的例子中counter的值就是這樣受到閉包的保護,只能經過closure方法去修改。

4、閉包的應用

閉包有兩個做用,一個是能夠訪問到局部變量,另外一個就是讓這些變量的值始終保持在內存中。咱們能夠用閉包來解決一個經典的問題:

for (var i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 10)
}
複製代碼

由於setTimeout是個異步函數,因此會先把循環所有執行完畢,這時候i就是6 了,因此會輸出一堆6。這個時候咱們就能夠用閉包去解決這個問題啦:

for (var i = 1; i <= 5; i++) {
    (function(j) {
        setTimeout(function timer() {
            console.log(j);
        }, 10)
    })(i)
}
複製代碼

在上面的代碼中,咱們首先使用了當即執行函數將i傳入函數內部,這個時候值就被固定在了參數j 上面不會改變,當下次執行timer這個閉包的時候,就可使用外部函數的變量j,從而達到目的。

固然,解決這個問題還能夠用let和傳入setTimeout的第三個參數,在這裏就不一一展開描述了。

閉包由於會把變量保存在內存中,能夠用來生成一個計時器,也就是文章開始的這個例子。

var add = (function () {
    var counter = 0;
    return function () {
        return counter += 1;
    }
})();
複製代碼

5、使用閉包須要注意些什麼?

一、閉包會使得函數中的變量都被保存在內存中,會增大內存的使用量,而且使用不當會容易形成內存泄漏。

由於,在JavaScript的垃圾自動回收機制中有這麼一句話:從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到它們。由於進入閉包的變量是有可能繼續使用的,即它們不會被自動回收,這個時候須要咱們手動回收清除。

二、若是不是由於某些特殊任務而須要閉包,在沒有必要的狀況下,在其它函數中建立函數是不明智的,由於閉包對腳本性能具備負面影響,包括處理速度和內存消耗。

最後,推薦觀看下面關於閉包的這篇文章:

developer.mozilla.org/zh-CN/docs/…

相關文章
相關標籤/搜索