閉包它在百度百科中的解釋原話是這樣的:閉包就是可以讀取其餘函數內部變量的函數。例如在javascript
中,只有函數內部的子函數才能讀取局部變量,因此閉包能夠理解成定義在一個函數內部的函數。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。javascript
下面來一一解讀這段話。java
首先來直接回答這個問題:閉包是什麼?閉包
答案:閉包是一個函數,什麼函數呢?一個定義在函數內部的函數。例如:異步
var add = (function () {
var counter = 0;
return function () {
return counter += 1;
}
})();
複製代碼
在上面的例子中,函數內部return
的這個函數function () {return counter += 1;}
,它就是一個閉包。咱們直接把add
在控制檯打印出來: 函數
add
,它其實是一個函數,可是這個函數倒是在另外一個函數內部建立的,這個就是
定義在函數內部的函數。
即咱們要解讀這句話:可以讀取其餘函數內部變量的函數。性能
咱們知道,在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
方法去修改。
閉包有兩個做用,一個是能夠訪問到局部變量,另外一個就是讓這些變量的值始終保持在內存中。咱們能夠用閉包來解決一個經典的問題:
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;
}
})();
複製代碼
一、閉包會使得函數中的變量都被保存在內存中,會增大內存的使用量,而且使用不當會容易形成內存泄漏。
由於,在JavaScript
的垃圾自動回收機制中有這麼一句話:從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到它們。由於進入閉包的變量是有可能繼續使用的,即它們不會被自動回收,這個時候須要咱們手動回收清除。
二、若是不是由於某些特殊任務而須要閉包,在沒有必要的狀況下,在其它函數中建立函數是不明智的,由於閉包對腳本性能具備負面影響,包括處理速度和內存消耗。
最後,推薦觀看下面關於閉包的這篇文章: