理解 JavaScript 中的閉包

理解 JavaScript 中的閉包

閉包是 JavaScript 的難點之一。大多數教程只告訴你,閉包就是一個函數中的另外一個函數,但這只是閉包的表象。本篇文章就帶你透過表象,看看閉包的本質。javascript

寫 JavaScript 而不知道什麼是閉包,就如同寫 Java 而不知道什麼是類 --- JSON之父 Douglas Crockfordjava

首先看看下面這一段代碼:瀏覽器

// 這段代碼理論上來說,是一個閉包
// 可是並不純正
var x = 0;
function addNumber() {
    return 1 + x;
}
複製代碼

上面這段代碼並非純正的閉包。x 定義在全局做用域中,因此並不能保證它不被修改。說到這裏要提一下,JavaScript 是一個詞法做用域的語言。這意味着在函數外定義的變量能夠在函數內使用,而反過來則不行。你不能在函數外部使用一個在函數內定義的變量。微信

下面的代碼是一個閉包,x 定義在函數外部。並且在函數執行完以後 addNumber 仍然能夠訪問到 x 的值。閉包

function closedFunction(x) {
    // 當咱們把代碼用函數包裹起來時
    // 就建立了一個函數做用域
    // 傳遞給函數的變量都是獨立的
    function addNumber() {
        return 3 + x;
    }
    return addNumber;
}
console.dir(closedFunction(3));
複製代碼

在瀏覽器中打印出來的值證實了 x 是一個閉包模塊化

那麼問題來了,若是消耗函數外部的變量就是閉包,那爲何下面這段代碼不是閉包呢?函數

function closedFunction(x) {
    var numberItem = x + 3;
    return numberItem;
}

console.dir(closedFunction)

複製代碼

當咱們調用函數時,會建立一個函數做用域併爲它分配內存。這個過程會一直持續到函數執行結束,內存被釋放爲止。在函數執行結束後,做用域中的值也將永遠消失。ui

變量的做用域不在函數外部,因此不構成閉包spa

可是若是用一個函數包裹另外一個函數,它將建立另外一個做用域 --- 它旨在告訴 JavaScript,在這個函數執行完以後不要當即銷燬它。3d

下面這段代碼中的變量 counter 是一個閉包,由於它在被調用的函數以外( Increment) 。

變量counter 是上述代碼片斷中的閉包

在某種程度上,閉包只是具備保留數據的函數。建立閉包實際上是在告訴JavaScript記住函數中事物的狀態 --- 只有被使用的變量纔會被視爲 閉包

由於閉包是有狀態函數,它們在被調用以後會記住其私有變量數據。因爲變量是私有的,外部函數沒法經過顯示調用來訪問這些私有變量。這樣能夠使整個函數自成一體,而且能夠保護其變量免受沒必要要的更改。

詞法做用域

所以,JavaScript 中的閉包是一種在無需顯示建立類的狀況下,就能將代碼模塊化且自包含的方式方法。使用閉包能夠實現代碼的可複製性,並且不用擔憂污染全局做用域。

閉包不單單是將一個函數放在另外一個函數中的行爲,它是一種用於建立可防止外部更改私有變量的技術,這些變量與程序的其餘部分隔離,而且具備持久性。

微信關注公衆號【React從入門到精通】

相關文章
相關標籤/搜索