ES6新特性之let和const

298 阅读3分钟

文章大部分是对javascript高级程序设计的总结。

let声明

块级作用域

let声明的范围是块作用域,而var声明的范围是函数作用域。

let声明的变量不能在if块外部被引用,是因为它的作用域仅限于该块内部。块作用域是函数作用域的子集,因此适用于var的作用域限制同样也适用于let。

if (true) {
  let age = 26;
  console.log(age);    // 26
}
console.log(age);      // ReferenceError: age没有定义

冗余声明

let也不允许同一个块作用域中出现冗余声明

var name;

var name;

let age;

let age; // SyntaxError;标识符age已经声明过了

暂时性死区

let声明的变量不会在作用域中被提升。

console.log(age); // ReferenceError:age没有定义
let age = 26;

在let声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone) ,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。

全局声明

let在全局作用域中声明的变量不会成为window对象的属性(var声明的变量则会)。

let age = 26;
console.log(window.age);   // undefined

条件声明

let的作用域是块,所以不可能检查前面是否已经使用let声明过同名变量,同时也就不可能在没有声明的情况下声明它。使用try/catch语句或typeof操作符也不能解决,因为条件块中let声明的作用域仅限于该块。

<script>
    let name = 'Nicholas';
    let age = 36;
</script>
<script>
    // 假设脚本不确定页面中是否已经声明了同名变量
    // 那它可以假设还没有声明过
    if (typeof name === 'undefined') {
        let name;
    }
    // name被限制在if {} 块的作用域内
    // 因此这个赋值形同全局赋值
    name = 'Matt';
    try {
        console.log(age); // 如果age没有声明过,则会报错
    }
    catch(error) {
        let age;
    }
    // age被限制在catch {}块的作用域内
    // 因此这个赋值形同全局赋值
    age = 26;
</script>

注意 不能使用let进行条件式声明是件好事,因为条件声明是一种反模式,它让程序变得更难理解。如果你发现自己在使用这个模式,那一定有更好的替代方式。

for循环中的let声明

在let出现之前,for循环定义的迭代变量会渗透到循环体外部:

// var
for (var i = 0; i < 5; ++i) {
  // 循环逻辑
}
console.log(i); // 5

// let 
for (let i = 0; i < 5; ++i) {
  // 循环逻辑
}
console.log(i); // ReferenceError: i没有定义

let声明迭代变量时,JavaScript引擎在后台会为每个迭代循环声明一个新的迭代变量。每个setTimeout引用的都是不同的变量实例,所以console.log输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。

// var
for (var i = 0; i < 5; ++i) {
  setTimeout(() => console.log(i), 0)
}
// 你可能以为会输出0、1、2、3、4
// 实际上会输出5、5、5、5、5

// let
for (let i = 0; i < 5; ++i) {
  setTimeout(() => console.log(i), 0)
}
// 会输出0、1、2、3、4

const声明

const声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。

const age // SyntaxError
const age = 26;
age = 36; // TypeError:给常量赋值
// const也不允许重复声明
const name = 'Matt';
const name = 'Nicholas'; // SyntaxError
// const声明的作用域也是块
const name = 'Matt';
if (true) {
  const name = 'Nicholas';
}
console.log(name); // Matt

const声明的限制只适用于它指向的变量的引用。如果是对象修改这个对象内部的属性并不违反const的限制,如果是数组修改这个数组中的元素也不违反const的限制。

const person = {};
person.name = 'Matt';   // ok
const list = [1,2,3]
list[0]=4	// ok

for-of和for-in循环

每次迭代会创建一个新变量

let i = 0;
for (const j = 7; i < 5; ++i) {
  console.log(j);
}
// 7, 7, 7, 7, 7
for (const key in {a: 1, b: 2}) {
  console.log(key);
}
// a, b
for (const value of [1,2,3,4,5]) {
  console.log(value);
}
// 1, 2, 3, 4, 5

总结

let

  • let是块级作用域,一个 { }(大括号)就是一个块
  • let也不允许同一个块作用域中出现冗余声明
  • let声明之前的执行瞬间被称为“暂时性死区”
  • let在全局作用域中声明的变量不会成为window对象的属性
  • 不要用let做条件声明
  • let声明迭代变量时,JavaScript引擎在后台会为每个迭代循环声明一个新的迭代变量。

const

  • const声明变量时必须同时初始化变量
  • const声明的基础类型变量不能被修改
  • const声明的限制只适用于它指向的变量的引用(修改对象内部属性与数组元素不被限制)
  • 在for-of和for-in循环中使用(每次迭代都会创建新变量)

最佳实践

  • 不使用var
  • const优先,let次之