告别 var,拥抱 let/const:一文彻底搞懂 JavaScript 变量与作用域

告别 var,拥抱 let/const:一文彻底搞懂 JavaScript 变量与作用域

在现代 JavaScript 开发中,var 已经被视为“历史遗留问题”,绝大多数团队和规范都强烈推荐使用 letconst

但很多人只是“知道要用 let/const”,却并不真正理解它们与 var 的本质区别,以及背后“作用域”和“变量提升”的真实机制。

这篇文章将从最基础的概念到最容易踩坑的细节,一次性帮你彻底搞懂。

一、三个关键字的核心对比表

特性varletconst
作用域函数作用域(function scope)块级作用域(block scope)块级作用域(block scope)
变量提升声明提升 + 初始化为 undefined声明提升,但有“暂时性死区”(TDZ)声明提升,但有暂时性死区(TDZ)
重复声明允许重复声明不允许在同一作用域重复声明不允许在同一作用域重复声明
重新赋值允许允许不允许(但对象/数组内容可变)
全局对象绑定是(挂载到 window/global)
推荐使用场景基本不推荐需要重新赋值的变量常量、引用不变的对象/数组
ES6 引入ES1(1997)ES6(2015)ES6(2015)

二、核心概念拆解

1. 作用域的本质区别

// var → 函数作用域
function test() {
  if (true) {
    var a = 100;
  }
  console.log(a); // 100
}
test();

// let/const → 块级作用域(if、for、while、{} 都算块)
function test2() {
  if (true) {
    let b = 200;
    const c = 300;
  }
  console.log(b); // ReferenceError: b is not defined
}
test2();

经典 for 循环陷阱(面试必考)

// var 版:全部输出 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 0);
}

// let 版:正确输出 0 1 2
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 0);
}

原因let 在每次循环都会创建一个新的块级作用域变量,而 var 只有一个函数作用域的 i。

2. 变量提升与暂时性死区(TDZ)

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

// 等价于
var a;
console.log(a);
a = 1;
console.log(b);   // ReferenceError: Cannot access 'b' before initialization
let b = 2;
const c = 3;
console.log(c);   // 正常
c = 4;            // TypeError: Assignment to constant variable

暂时性死区(Temporal Dead Zone)
letconst 的声明会被提升到块顶部,但在声明语句执行之前,访问它们会抛出 ReferenceError。这就是“暂时性死区”。

3. const 的真正含义

const 限制的是“变量的引用”不可变,而不是值不可变

const obj = { name: "小明" };
obj.age = 18;          // 允许
obj = { name: "小红" }; // TypeError

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

最佳实践

  • 需要重新赋值的变量 → 用 let
  • 不会重新赋值的变量 → 一律用 const(包括对象、数组)

三、常见面试/真实开发中的坑

  1. var 在 for 循环中的经典错误(已上面展示)
  2. 立即执行函数解决 var 问题(老写法)
for (var i = 0; i < 3; i++) {
  (function (i) {
    setTimeout(() => console.log(i), 0);
  })(i);
}
  1. let 在 for 循环中的神奇行为
let funcs = [];
for (let i = 0; i < 3; i++) {
  funcs.push(function () {
    console.log(i);
  });
}
funcs[0](); // 0
funcs[1](); // 1
funcs[2](); // 2
  1. const 定义的对象可以修改内容,但不能重新指向
  2. 全局 var 与 let 的区别
var x = 1;      // window.x = 1
let y = 2;      // window.y 不存在

console.log(window.x); // 1
console.log(window.y); // undefined

四、现代 JavaScript 变量声明推荐原则(2024–2025)

  1. 默认使用 const
  2. 当需要重新赋值时才改用 let
  3. 永远不要再用 var(除非维护 10 年前的遗留代码)
  4. 在 for…of、for…in、forEach 等循环中优先使用 const(迭代变量通常不需要重新赋值)
// 推荐写法
const users = await fetchUsers();
for (const user of users) {
  console.log(user.name);
}

const config = { timeout: 5000 };
config.retry = 3; // 允许

五、总结一句话口诀

  • var:函数作用域 + 提升 + 可重复声明 → 容易出 Bug → 淘汰
  • let:块级作用域 + 有 TDZ + 可重新赋值 → 替代 var 的主要工具
  • const:块级作用域 + 有 TDZ + 引用不可变 → 现代默认选择

记住这一句
“能用 const 就用 const,需要改值才用 let,var 已经可以去博物馆了。”

如果你还有下面这些具体疑问,欢迎继续问:

  • let/const 在 for 循环的底层实现到底是什么?
  • 暂时性死区(TDZ)在编译阶段是怎么处理的?
  • const 对象/数组到底能改到什么程度?
  • 实际项目中如何迁移 var 到 let/const?

一起把作用域和变量彻底搞清楚!

文章已创建 4455

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部