這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰markdown
MDN 對閉包的定義爲:閉包
閉包是指那些可以訪問自由變量的函數。app
那什麼是自由變量呢?異步
自由變量是指在函數中使用的,但既不是函數參數也不是函數的局部變量的變量。ide
由此,咱們能夠看出閉包共有兩部分組成:函數
閉包 = 函數 + 函數可以訪問的自由變量post
簡單來說:ui
閉包就是可以讀取其餘函數內部變量的函數lua
在函數 A 中還有函數 B,函數 B 調用了函數 A 中的變量,那麼函數 B 就稱爲函數 A 的閉包。url
function foo() {
let a = 2;
function bar() {
console.log(a);
}
bar();
}
foo(); // 2
複製代碼
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log("i: ", i);
}, 1000);
}
console.log(i);
// 輸出
// 5;
// i: 5;
// i: 5;
// i: 5;
// i: 5;
// i: 5;
複製代碼
要理解上述代碼,首先咱們得了解一下概念:
上述代碼中使用定時器,當 JS 引擎線程執行到該段代碼,便把定時器放到定時器線程去計時,此時 JS 引擎線程執行同步棧裏面的任務。當定時器計時完成以後,將回調函數推入消息隊列。等待棧中的代碼執行完畢以後會去讀取消息隊列中的事件。
因爲 JS 的函數做用域,當回調函數被推入消息隊列的時候沒有帶上參數。for 循環結束以後,由於 i 是用 var 定義的,因此 var 是全局變量(這裏沒有函數,若是有就是函數內部的變量),這個時候的 i 是 5。
如何解決?
// let 是塊級做用域,當前塊是循環體
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log('i: ',i);
}, 1000);
}
console.log(i);
// 輸出
// 5
// i: 0
// i: 1
// i: 2
// i: 3
// i: 4
複製代碼
//這是函數級做用域,利用閉包模擬塊級做用域
for (var i = 0; i < 5; i++) {
(function(){
var j = i;
setTimeout(function() {
console.log('i: ',j);
}, 1000);
})();
}
console.log(i);
// 輸出
// 5
// i: 0
// i: 1
// i: 2
// i: 3
// i: 4
複製代碼
// 或者採起傳參的方式,使用了閉包
for (var i = 0; i < 5; i++) {
(function(j){
setTimeout(function() {
console.log('i: ',j);
}, 1000);
})(i);
}
console.log(i);
// 輸出
// i is not defined
// i: 0
// i: 1
// i: 2
// i: 3
// i: 4
複製代碼
// setTimeout 的第三個參數
for (var i = 0; i < 5; i++) {
setTimeout(function timer(j) {
console.log('i: ', j);
}, 1000, i);
}
console.log(i);
// 輸出
// i is not defined
// i: 0
// i: 1
// i: 2
// i: 3
// i: 4
複製代碼