JavaScript 变量声明全面解析:从 `var` 到 `let` 与 `const`

211 阅读3分钟

JavaScript 变量声明终极指南:彻底搞懂 varletconst

变量声明是 JavaScript 语言演进中最重要的升级点之一。从最初只能使用 var,到 ES6 引入 letconst,JS 的作用域机制、可维护性与可预测性都发生了巨大的变化。本文将从原理层面梳理三者的差异,并结合常见错误与最佳实践,帮助你构建现代 JS 的变量使用思维模型。


一、变量声明的时代分水岭

1. ES5 时代:只有 var

早期 JS 使用 var 声明变量:

var age = 18;
var PI = 3.1415926;

其特点是:

  • 函数级作用域,不支持块级作用域
  • 变量提升:声明会被提升到当前作用域顶部
  • 可重复声明,容易污染作用域

这导致大型项目中难以维护,语义也不清晰。


2. ES6 引入 letconst

为了修复 var 的缺陷,ES6 带来了现代声明方式:

  • let可变变量
  • const常量绑定
let count = 10;
const TOKEN = "abc123";

它们不仅提供了更清晰的语义,还引入了「块级作用域」与「暂时性死区」。


二、编译阶段 vs 执行阶段:理解变量异常的关键

JavaScript 代码执行分两步:

  1. 编译阶段:扫描、注册变量与函数
  2. 执行阶段:按顺序执行逻辑、赋值

varletconst 的行为差异全部源于这两个阶段。


三、var 的行为模型

1. 变量提升(Hoisting)

console.log(a); // undefined
var a = 1;

编译等价于:

var a;
console.log(a);
a = 1;

声明被提升,赋值不会被提升
这是导致很多初学者困惑的根源。


2. 函数级作用域

{
  var x = 100;
}
console.log(x); // 100

块级作用域({})完全无法隔离变量。


四、letconst:现代 JS 的根基

1. 块级作用域(Block Scope)

{
  let h = 180;
  const PI = 3.14;
}
console.log(h);  // ReferenceError

作用域更安全、更可控。


2. 暂时性死区(TDZ)

console.log(n); // ReferenceError
let n = 10;

变量在声明前不可访问,直接杜绝了变量提升导致的隐性 bug。


3. const 的特殊性

const obj = { a: 1 };
obj.a = 2; //  允许修改内部属性
obj = {}; //  不允许重新赋值

如果你需要真正的只读对象:

Object.freeze(obj);

五、常见错误拆解

错误原因
ReferenceError: 未定义变量跨作用域访问
ReferenceError: before initialization触发 TDZ
TypeError: Assignment to constant variable修改 const 绑定

调试建议:

  • 永远不要在声明前访问变量
  • 尽量减少变量作用域跨度
  • 只在确实需要可变时使用 let

六、函数提升与表达式差异

fn(); //  正常
function fn() {}

bar(); //  ReferenceError
let bar = function(){};
  • 函数声明:整体提升
  • 函数表达式:受制于变量声明方式,会触发 TDZ

七、实用最佳实践

场景建议
默认情况使用 const
需要修改变量时使用 let
避免使用var
对象需要不可变Object.freeze()
不要把“声明”和“使用”分得太开避免 TDZ

八、现代 JS 的变量哲学

var 是历史遗留物,容易导致隐性 bug。
let/const 提供了更可靠、更可预测的编程体验。

牢记三条黄金法则:

  1. 默认用 const
  2. 确实需要改变值时用 let
  3. 永远不要提前访问变量

掌握这套模型,你的 JS 代码质量将提升一个量级。