块级作用域:let 和 const 的正确使用

ES6 (ECMAScript 2015) 引入了两个重要的新关键字:letconst,它们彻底改变了 JavaScript 的作用域规则,为开发者提供了更精确的变量声明方式。本文将深入探讨块级作用域的概念以及如何正确使用 letconst

理解块级作用域

在 ES6 之前,JavaScript 只有函数作用域和全局作用域,使用 var 声明的变量要么是函数级的,要么是全局的。ES6 引入了块级作用域,即由一对花括号 {} 界定的代码块(如 if 语句、for 循环等)形成的作用域。

javascript 复制代码
{
  // 这是一个块级作用域
  let x = 10;
  console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined

let 关键字

let 允许你声明一个块级作用域的局部变量:

  1. 块级作用域let 声明的变量只在声明它的块或子块中可用
  2. 不存在变量提升:与 var 不同,let 声明的变量不会提升到作用域顶部
  3. 暂时性死区:在声明前访问 let 变量会导致 ReferenceError
  4. 不允许重复声明:在同一作用域内不能重复声明同名变量
javascript 复制代码
// 示例1:块级作用域
function letExample() {
  if (true) {
    let y = 20;
    console.log(y); // 20
  }
  console.log(y); // ReferenceError: y is not defined
}

// 示例2:for循环中的let
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100); // 输出0,1,2,3,4
}

const 关键字

const 用于声明块级作用域的常量:

  1. 必须初始化:声明时必须赋值
  2. 不可重新赋值:一旦赋值,不能改变(对于基本类型)
  3. 不是完全不可变:对于对象和数组,内容可以修改,只是不能重新赋值
  4. 其他特性:与 let 相同的块级作用域、暂时性死区等特性
javascript 复制代码
// 基本类型
const PI = 3.14159;
PI = 3; // TypeError: Assignment to constant variable

// 对象和数组
const person = { name: 'Alice' };
person.name = 'Bob'; // 允许
person = { name: 'Charlie' }; // TypeError

const numbers = [1, 2, 3];
numbers.push(4); // 允许
numbers = [5, 6]; // TypeError

使用建议

  1. 默认使用 const:除非你知道变量需要重新赋值,否则优先使用 const
  2. 需要重新赋值时用 let:当变量需要被重新赋值时使用 let
  3. 避免使用 var:在现代 JavaScript 开发中,基本不再需要使用 var
  4. 注意对象和数组的"常量性"const 只保证变量引用的不变性,不保证引用对象的不变性

常见误区

  1. 认为 const 创建不可变对象:实际上只是创建不可重新赋值的变量
  2. 在循环中使用不当for...infor...of 中可以使用 const,但传统 for 循环需要 let
  3. 过度使用 let:很多情况下 const 已经足够
javascript 复制代码
// 正确使用const的循环
const arr = [1, 2, 3];
for (const item of arr) {
  console.log(item);
}

// 需要let的循环
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

总结

letconst 的引入使 JavaScript 的作用域管理更加清晰和可靠。通过理解块级作用域的概念并遵循最佳实践,可以编写出更安全、更易维护的代码。记住:默认使用 const,需要重新赋值时用 let,尽量避免使用 var