閉包是 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 中的閉包是一種在無需顯示建立類的狀況下,就能將代碼模塊化且自包含的方式方法。使用閉包能夠實現代碼的可複製性,並且不用擔憂污染全局做用域。
閉包不單單是將一個函數放在另外一個函數中的行爲,它是一種用於建立可防止外部更改私有變量的技術,這些變量與程序的其餘部分隔離,而且具備持久性。