JavaScript 中的变量声明:var、let 与 const 的全面解析

发布于:2025-05-30 ⋅ 阅读:(21) ⋅ 点赞:(0)

JavaScript 中的变量声明:var、let 与 const 的全面解析

在 JavaScript 的发展历程中,变量声明方式经历了从 varletconst 的演变。这些关键字不仅影响代码的可读性,还深刻决定了变量的作用域、生命周期以及潜在的错误风险。本文将深入解析这三种声明方式的特性、使用场景及注意事项,助你写出更安全、更高效的 JavaScript 代码。


一、var:函数作用域的“老派”选手

1. 特性

  • 函数作用域var 声明的变量仅在声明它的函数内有效。即使在 iffor 等块级作用域内声明,其作用域仍会“溢出”到整个函数。
  • 变量提升(Hoisting)var 的声明会被提升到作用域顶部,但赋值不会提升。这可能导致在声明前访问变量时返回 undefined
  • 允许重复声明:同一作用域内多次声明同名变量不会报错,但后声明的会覆盖前一个。

2. 示例

function exampleVar() {
  console.log(x); // 输出 undefined(变量提升)
  var x = 10;
  console.log(x); // 输出 10
}
exampleVar();

// 函数作用域溢出
function test() {
  if (true) {
    var y = 20;
  }
  console.log(y); // 输出 20(块级作用域失效)
}
test();

3. 注意事项

  • 全局污染风险:在函数外部声明的 var 变量会成为全局变量(挂载到 window 对象),容易引发命名冲突。
  • 循环中的陷阱:在 for 循环中使用 var 声明的变量会“泄漏”到循环外部,导致意外行为。
    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 100);
    }
    // 输出 3 次 3(i 的最终值为 3)
    

4. 应用场景

  • 旧代码兼容:在需要兼容旧浏览器或遗留项目时使用。
  • 动态作用域需求:需要变量在整个函数范围内可见时。

二、let:块级作用域的“安全卫士”

1. 特性

  • 块级作用域let 声明的变量仅在声明它的代码块(如 iffor 等)内有效。
  • 无变量提升let 的声明不会被提升到作用域顶部,声明前访问会抛出 ReferenceError(暂时性死区,TDZ)。
  • 禁止重复声明:同一作用域内不能重复声明同名变量。

2. 示例

function exampleLet() {
  if (true) {
    let x = 10;
    console.log(x); // 输出 10
  }
  console.log(x); // 报错:x 未定义(块级作用域)
}
exampleLet();

// 循环中的安全变量
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出 0, 1, 2(每次迭代绑定独立的 i)

3. 注意事项

  • 避免 TDZ 陷阱:在声明前访问变量会导致运行时错误,需注意变量的声明顺序。
  • 块级作用域限制:在嵌套作用域中需谨慎处理变量的可见性。

4. 应用场景

  • 循环计数器:避免变量泄漏,确保每次迭代的变量独立。
  • 条件分支中的局部变量:防止变量污染外层作用域。

三、const:常量声明的“终极方案”

1. 特性

  • 块级作用域:与 let 一致,const 声明的常量仅在声明它的代码块内有效。
  • 不可重新赋值:声明时必须初始化,且不能再次赋值。但若声明的是对象或数组,其内部属性或元素可以修改。
  • 无变量提升:同样存在暂时性死区,声明前访问会抛出错误。

2. 示例

// 基础类型常量
const PI = 3.14;
PI = 3.14159; // 报错:Assignment to constant variable

// 引用类型常量
const obj = { name: "Alice" };
obj.name = "Bob"; // 允许修改属性
obj = {}; // 报错:Assignment to constant variable

// 数组常量
const arr = [1, 2, 3];
arr.push(4); // 允许修改数组内容
arr = [4, 5]; // 报错:Assignment to constant variable

3. 注意事项

  • 初始化必须赋值const 声明时必须立即赋值,否则会报错。
  • 引用类型的“伪常量”:虽然不能重新赋值,但对象或数组的内部内容可以修改,需根据需求选择是否使用。

4. 应用场景

  • 不可变常量:如数学常数、配置项等。
  • 对象/数组的固定引用:需要保留引用地址不变,但允许修改内部内容时。

四、var、let、const 的对比与选择

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
变量提升 是(仅声明) 否(存在 TDZ) 否(存在 TDZ)
重复声明 允许 不允许 不允许
可重新赋值
推荐使用场景 旧代码兼容 需要重新赋值的变量 不可变的常量

最佳实践

  1. 优先使用 const:默认情况下用 const 声明变量,确保变量不可变,减少意外修改的风险。
  2. 需要重新赋值时用 let:如循环计数器、动态变量等。
  3. 避免使用 var:除非需要兼容旧代码或利用其函数作用域特性。

五、高频面试考点解析

1. 作用域与变量提升

  • 问题var 声明的变量为何可以在声明前访问?
    • 答案var 的声明会被提升到作用域顶部,但赋值不会提升,因此在声明前访问会返回 undefined
  • 问题letconst 的暂时性死区(TDZ)是什么?
    • 答案letconst 的声明不会被提升,声明前访问会抛出 ReferenceError,这一区域称为 TDZ。

2. 块级作用域的应用

  • 问题:为什么在 for 循环中使用 let 而不是 var
    • 答案var 声明的变量会“泄漏”到循环外部,导致所有迭代共享同一个变量;let 会为每次迭代创建独立的变量副本。

3. const 的“伪常量”

  • 问题const 声明的对象为何可以修改属性?
    • 答案const 保证的是变量引用地址不变,而对象或数组的内部内容是可变的。如果需要完全不可变,需结合 Object.freeze()

六、总结

  • var:函数作用域、变量提升、允许重复声明,适合旧代码兼容。
  • let:块级作用域、无变量提升、禁止重复声明,适合需要重新赋值的变量。
  • const:块级作用域、不可重新赋值、适合声明常量,推荐优先使用。

在现代 JavaScript 开发中,letconst 几乎完全取代了 var,它们提供了更细粒度的作用域控制和更安全的变量声明方式。通过合理选择声明关键字,可以显著提升代码的可读性、可维护性和健壮性。

记住:变量声明的选择,是代码质量的第一步!