ES6(一):let和const
ES6(一):let和const
一、let
1. let基本用法
相當於var,但是又與var不同,因為let宣告的變數只能在let所在的程式碼塊中有效。
從以下兩段程式碼以及對應的輸出結果可以很明顯的看出var與let的區別。
code:
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
result:
0
1
2
3
4
5
code:
for (let i = 0; i < 5; i++) {
console.log(i);
}
console. log(i);
result:
0
1
2
3
4
ReferenceError: i is not defined
2. 塊級作用域
眾所周知,在ES5中只有全域性作用域和函式作用域,沒有我們所謂的塊級作用域。
這不禁讓我想起一個面試題:
code:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
問:上面這段程式碼輸出的結果是什麼?
仔細琢磨一下我們很容易得出正確的答案:
result:
2018-10-31T06:55:30.020Z 5
2018-10-31T06:55:31.021Z 5
2018-10-31T06:55:31.021Z 5
2018-10-31T06:55:31.022Z 5
2018-10-31T06:55:31.022Z 5
2018-10-31T06:55:31.022Z 5
仔細看一下發現:由於setTimeout會被JavaScript延遲執行,因此是先輸出最底部的console.log,隔一秒之後再執行迴圈裡面的consol.log,此時i已經全部是5。
那我們怎麼樣才能使輸出結果變成想要的0,1,2,3,4呢?
明白了let的原理之後其實問題變得很簡單:
code:
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
這樣就行了嗎?
result:
ReferenceError: i is not defined
哈哈,剛剛還說過let只在所在的程式碼塊裡面有效,這裡最外層的console.log很明顯找不到i,所以報出引用錯誤。想要得到正確答案其實還需要去掉最後一行console.log。
當然這個題目要擱以前的話,首先想到的解決方案應該是IIFE(Immediately Invoked Function Expression:宣告即執行的函式表示式)來解決閉包造成的問題。
code:
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(new Date, j);
}, 1000);
})(i);
}
console.log(new Date, i);
result:
2018-10-31T07:17:45.825Z 5
2018-10-31T07:17:46.827Z 0
2018-10-31T07:17:46.827Z 1
2018-10-31T07:17:46.827Z 2
2018-10-31T07:17:46.827Z 3
2018-10-31T07:17:46.827Z 4
由此我們是不是就可以得出一個結論,有了let,我們就可以拋棄IIFE了?
這個暫時還不好說,我們還是繼續看看let的其他特性。
3. 沒有變數提升
var命令會產生變數提升的現象,這使得js這門語言變得並不嚴謹,這一點是特點也是雞肋。用var宣告的變數,如果我們在宣告之前使用,則它的值為undefined。
而當我們用let定義變數時,就必須嚴格按照先定義再使用的原則了,反之則會丟擲一個大大的引用錯誤(ReferenceError),顯然這更符合人們的使用習慣。
code:
console.log(a);
var a = 2;
console.log(b);
let b = 2;
result:
undefined
ReferenceError: b is not defined
4. 暫時性死區及不能重複宣告
ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。
總之,在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。
“暫時性死區”也意味著typeof不再是一個百分之百安全的操作。
let不允許在相同作用域內,重複宣告同一個變數。
二、const
1. const基本用法
const命令是宣告一個常量,用法和let一樣。
- 和let的相同之處是:
- const與let作用域相同,都是隻在宣告的程式碼塊中起作用
- const也不會提升所宣告的常量
- const也不能重複宣告
- 不同之處是const宣告的常量在宣告時就必須賦值,因為一旦宣告就不能改變改常量的值
2. 真的不能改變嗎?
const實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址所儲存的資料不得改動。對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指向實際資料的指標,const只能保證這個指標是固定的(即總是指向另一個固定的地址),至於它指向的資料結構是不是可變的,就完全不能控制了。因此,將一個物件宣告為常量必須非常小心。
code&result:
const a=1;
a=2;//TypeError
const obj={
b:3,
c:4
}
obj.b=5;
console.log(obj);//{b:5,c:4}
參考資料
- 《ECMAScript 6 入門》——阮一峰
- 破解前端面試(80% 應聘者不及格系列):從閉包說起——王仕軍知乎專欄:前端週刊
- ECMAScript2015官方標準