理解Javascript的閉包
大綱
- 閉包真言
- 理解閉包情景一:函式作為返回值
- 理解閉包情景二:函式作為引數傳遞到其他函式中
- 理解閉包情景三:迴圈和閉包
- 實際開發中閉包的應用
- 閉包優缺點
閉包真言
- JavaScript中閉包無處不在,你只需要能夠識別並擁抱它。
- 當函式可以記住並訪問所在的詞法作用域,即使函式是在當前詞法作用域之外執行,這時就產生了閉包。
理解閉包需要搞懂JS的作用域,。
理解閉包
閉包情景一:函式作為返回值
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // 2
我們知道 bar
函式里面的 a
變數在外面是訪問不到的,但呼叫 baz
方法為什麼仍然能輸出 2
呢?
原因:
- 首先,呼叫
foo
函式把bar
方法函式作為返回值,且賦值了給baz
變數。 - 然後,
baz
變數就存在了對bar
函式的引用。 - 最後,呼叫
baz
方法時,a
去它的父級作用域(foo
函式)查詢得到該值,所以就輸出了a
值。
這就是閉包的作用之一:把函式作為返回值,外部的作用域可以能訪問到函式作用域內部的變數。
閉包情景二:函式作為引數傳遞到其他函式中
function foo() {
var a = 2;
function baz() {
console.log(a); // 2
}
bar(baz);
}
function bar(fn) {
fn();
}
foo()
函式作為引數傳遞到其他函式中的原理和函式作為返回值差不多:
-
foo
函式的bar
方法把內部的baz
方法傳入外部的bar
方法中。 - 外部的
bar
方法呼叫了傳入的baz
方法,a
去它的父級作用域(foo函式)查詢得到值,所以就輸出了a
值。
這就是閉包的作用之二:函式作為引數傳遞到其他函式中,外部函式作用域也可以訪問到其他函式內部作用域的變數。
閉包情景三:迴圈和閉包
需求:
在迴圈裡面新增一個定時器,想預期每秒列印一次對應的值,如按順序輸出1,2,3,4,5
程式碼:
以下程式碼是不能完成預期結果,都輸出 6,6,6,6,6
,原因:i
都是同一個引用for迴圈最後的結果: 6
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
改進方案1
- 把
i
當做引數傳入一個立即執行函式中,立即執行函式每次迭代時都建立一個新的作用域。 - 這時候
i
擁有了自己的函式內部作用域,儲存到每個對應的i
值 ,就能到達預期的輸出1, 2, 3, 4, 5
結果。
for (var i=1; i<=5; i++) {
(function(j) {
setTimeout( function timer() {
console.log( j );
}, j*1000 );
})( i );
}
改進方案2
方案1可知,使用ES6的 let
宣告也有塊級作用域。
for迴圈頭部的let宣告,每次迭代都會宣告一次 i
,那麼每個 i
就有了自己的作用域,也能達到預期輸出結果。
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
這就是閉包的作用之三:能讓一個變數長期儲存在記憶體中。
實際開發中閉包的應用
收斂許可權
// 檢測一個數字是否被使用過
function isFirstLoad() {
var _list = []
return function (id) {
if(_list.indexOf(id) >= 0) {
return false
}else {
_list.push(id)
return true
}
}
}
// 使用
// 如果第一次使用該數字則返回true,反之返回false
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
建立10個標籤,點選的時候彈出來對應的序號
// 建立10個<a>標籤,點選的時候彈出來對應的序號
for (var i = 0; i < 10; i++) {
(function (i) {
var a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
console.log(i)
})
document.body.appendChild(a)
})(i)
}
閉包優缺點
優點
- 能讓一個變數長期儲存在記憶體中。
- 避免全域性變數的汙染。
- 私有成員的存在。
缺點
- 常駐記憶體,增加記憶體使用量。
- 使用不當會很容易造成記憶體洩露。
參考連結
- by, 雙越老師
- , by 雲霏霏
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1747/viewspace-2822851/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 理解JavaScript 閉包JavaScript
- 理解 JavaScript 閉包JavaScript
- 對javascript閉包的理解JavaScript
- javascript閉包的個人理解JavaScript
- 理解 JavaScript 中的閉包JavaScript
- 面試:對javascript的閉包的理解面試JavaScript
- 深入理解javascript原型和閉包(15)——閉包JavaScript原型
- javascript閉包的理解和例項JavaScript
- 全面理解Javascript閉包和閉包的幾種寫法及用途JavaScript
- 【譯】理解JavaScript閉包——新手指南JavaScript
- 深入理解javascript系列(七):閉包(1)JavaScript
- 深入理解javascript系列(八):閉包(2)JavaScript
- 深入理解javascript原型和閉包系列JavaScript原型
- 深入理解javascript原型和閉包(10)——thisJavaScript原型
- JavaScript 的閉包JavaScript
- [JavaScript閉包]Javascript閉包的判別,作用和示例JavaScript
- 理解“閉包”
- 理解閉包
- PHP 閉包的理解PHP
- js閉包的理解JS
- 深入理解javascript系列(九):應用閉包JavaScript
- 聽說你還不理解JavaScript閉包JavaScript
- 【譯】JavaScript進階 從實現理解閉包JavaScript
- 帶你一分鐘理解 JavaScript 閉包JavaScript
- 深入理解javascript原型和閉包(17)——補thisJavaScript原型
- 深入理解javascript原型和閉包(完結)JavaScript原型
- JavaScript閉包JavaScript
- JavaScript 閉包JavaScript
- JavaScript - 閉包JavaScript
- 聽說你還不理解JavaScript裡的閉包JavaScript
- Groovy閉包理解
- 對JS閉包的理解JS
- Golang中閉包的理解Golang
- 深入理解javascript系列(十):模組化與閉包JavaScript
- 深入理解javascript原型和閉包(16)——完結JavaScript原型
- 深入理解javascript原型和閉包(6)——繼承JavaScript原型繼承
- 深入理解javascript原型和閉包(5)——instanceofJavaScript原型
- 深入理解javascript原型和閉包(3)——prototype原型JavaScript原型