因為ES6之前存在變量提升問題,容易造成問題,ES6引入了塊級作用域。
塊級聲明
塊級作用域在函數或者塊({})中創建。
let聲明,不提升,不可重復聲明。
例子1(報錯):
var count = 30;
// throws an error
let count = 40;
例子2(不報錯,在塊中創建一個局部變量count,屏蔽了全局count直到從塊中出來):
var count = 30;
if (condition) {
// doesn't throw an error
let count = 40;
// more code
}
const聲明,不提升,不可重復聲明。聲明的同時必須初始化,不可更改。
但是const聲明的對象屬性值是可以修改的,只是綁定不能修改。
例子:
const person = {
name: "Nicholas"
};
// works
person.name = "Greg";
// throws an error
person = {
name: "Greg"
};
暫存死區(Temporal Dead Zone,TDZ)
let和const聲明的變量只能在聲明之后調用。在聲明之前引用會報引用錯誤,即使是用typeof之類的安全操作也不行。
例子:
if (condition) {
console.log(typeof value); // throws an error
let value = "blue";
}
當JS引擎看到一個即將執行的代碼塊時,對var聲明的變量做聲明提升,而對let和var聲明的變量則將聲明放到一個暫存死區,任何在之前嘗試讀取TDZ中變量的操作都會導致運行時錯誤。直到執行流到let和var的定義,相應變量才從TDZ移除。
但是下面的例子不會報錯,因為引用變量時它不在TDZ,即使沒有也只是返回undefined。
例子:
console.log(typeof value); // "undefined"
if (condition) {
let value = "blue";
}
TDZ只是區塊綁定的一個特別方面。另外一個在于循環當中。
循環中的區塊綁定
在for循環中使用let聲明的變量,在每次循環事都綁定了不同的變量。例子:
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
})
如果是var聲明的i,那輸出結果就會是10個10,只能使用IIFE來解決。對于for in和for of同樣適用。例子:
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (let key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // outputs "a", then "b", then "c"
});
如果是var key in object的話輸出回事3個c。
let在for循環中的這個特性是ES6特別制定的,和變量提升無關。
對于const,在for循環中會報錯,因為聲明的變量不可修改,但在for in和for of中表現和let一致。例子:
var funcs = [];
// throws an error after one iteration
for (const i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
全局區塊綁定
使用let和const聲明的全局變量不會綁定到window的屬性,但使用var聲明的全局變量會綁定到window的屬性。
例子:
// in a browser
let RegExp = "Hello!";
console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
const ncz = "Hi!";
console.log(ncz); // "Hi!"
console.log("ncz" in window); // false
最佳實例
聲明變量盡量使用const,除非你確定需要修改才使用let。