一、let和var的區別
//let和var的區別相信列位已經耳熟能詳了,在這裏不做過多陳述,在第二段將用到的會提及一下
二、在定時器中的表現
- var
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log("定時器" + i);
})
}
//5 * 定時器5
- let
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log("定時器" + i);
})
}
//定時器0
//定時器1
//定時器2
//定時器3
//定時器4
三、解析
(1)產生這種的原因有三個
- let聲明帶來的塊級作用域
- for循環機制(重點)
- var的變量提升
(2) var命令做了什麼
- var首先帶來了變量提升,將i提升到全局變量,所以var命令實際上只執行了一次
//在es6之前,js只有全局和局部作用域概念,局部作用也可以理解成函數作用域,所以此時在for循環用var聲明的變量是全局變量 - 其次定時器是宏任務隊列,for循環在這裏相當於在Event Queue添加了五個事件
- 最後主線程任務執行結束後開始調用Event Queue中的事件,因爲var帶來的變量提升,此時全局只能有一個變量i,而i在for循環迭代到5,所以此時5個setTimeout事件中的i都指向了5
(3)let命令做了什麼
- let命令不存在變量提升,所以在每次for循環執行中let都會執行一次
- let塊級作用域鎖區,所以當前的作用域內也只有一個i
- 所以每個setTimeout中的i值都指向了不同的值
(4)for命令做了什麼
- for 循環機制決定了它每次循環都是不同的塊級作用域(這裏與let聲明塊級作用域吻合)
- 根據阮一峯的<<ES6入門>>:JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。(實現了i初始化)
四、總結
簡單點說,實現輸出0 1 2 3 4的需求的原理就是利用了js中for循環機制和let的無變量提升和塊級作用域
實際上 用 let 也能實現 var 同樣的需求(代碼如下)
let i = 0;
for (; i < 5; i++) {
setTimeout(function () {
console.log("定時器" + i);
})
}
//5 * 定時器5
//這裏能實現的原因就是手動實現了一個變量提升
//此時在setTimeout事件中都是對i的引用而不是賦予一個新值