因为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。
网友评论