ES6之 let,const
var 声明与变量提升
var是js的变量声明语句,使用var声明的变量,无论其声明的实际语句在何处,都会被提升到所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部),这种特性就叫做变量提升。声明式函数也有同样特性
我们来解释一下变量提升:
// 浏览器V8处理前
console.log(single) //undefined
var single=1
// 浏览器V8引擎处理后,实际执行顺序如下
var single;
console.log(single) //undefined
single=1
很明显,single无论在哪声明都会被提升到所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部)
变量提升的处理如下:变量声明=>初始化赋值
块级声明
块级声明也就是让所声明的变量在指定块的作用域外无法被访问。块级作用域(又被称为词法作用域)在如下情况被创建:
- 在一个函数内部
- 在一个代码块(由一对花括号包裹)内部
块级作用域是很多类 C 语言的工作机制, ES6 引入块级声明,是为了给 JS 添加灵活性以及与其他语言的一致性。
let 声明
let 声明的语法与 var 的语法一致。你基本上可以用let来代替var进行变量声明,但会将变量的作用域限制在当前代码块中
- let没有变量提升,因此你需要手动将 let 声明放置到顶部
- 在同一块级作用域中let禁止重复声明
- let不会覆盖全局变量,而va声明的变量会,如window,因此少用var去声明全局变量
- let会有块级作用域绑定,因为let没有变量声明,因此在let未声明定义之前,变量不能访问,这又叫暂时性死区(TDZ)
const常量声明
功能与let基本相似,不同之处如下
- let声明变量,const声明常量
- const 声明会阻止对于变量绑定与变量自身值的修改,但不会阻止对变量成员的修改
循环内的 let 声明
规范中规定了 let 声明在循环内部的行为:在每次迭代中,都会创建一个新的 同名变量并对其进行初始化。
大家都众所周知的例子,依次循环打印0-9,现在用let可以这样写:
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 从 0 到 9 依次输出
})
而以前用var得这么写:
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // 从 0 到 9 依次输出
});
所以,以后循环多用let吧!
那么用const行不行呢?如下:
var funcs = [];
// 会报错!!
for (const i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 从 0 到 9 依次输出
})
为什么会报错?const 声明会阻止对于变量绑定与变量自身值的修改
总结
let 与 const 块级绑定将词法作用域引入了 JS 。这两种声明方式都不会进行提升,并且 只会在声明它们的代码块内部存在。由于变量能够在必要位置被准确声明,其表现更加接近 其他语言,并且能减少无心错误的产生。作为一个副作用,你不能在变量声明位置之前访问 它们,即便使用的是 typeof 这样的安全运算符。由于块级绑定存在暂时性死区( TDZ ), 试图在声明位置之前访问它就会导致错误。
et 与 const 的表现在很多情况下都相似于 var ,然而在循环中就不是这样。在 for-in 与 for-of 循环中, let 与 const 都能在每一次迭代时创建一个新的绑定,这意味着在循 环体内创建的函数可以使用当前迭代所绑定的循环变量值(而不是像使用 var 那样,统一使 用循环结束时的变量值)。这一点在 for 循环中使用 let 声明时也成立,不过在 for 循 环中使用 const 声明则会导致错误。
块级绑定当前的最佳实践就是:在默认情况下使用 const ,而只在你知道变量值需要被更改 的情况下才使用 let 。这在代码中能确保基本层次的不可变性,有助于防止某些类型的错 误。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。