深入理解 ES6 的 let 和 const:告别 var 的坑,拥抱现代 JavaScript
作为前端开发者,你是否曾被
var的“奇怪行为”折磨得怀疑人生?从 ES6(2015)开始,let和const已成为现代 JavaScript 的标配。本文用真实代码+错误分析,带你彻底告别var的坑,写出更安全、可读性更高的代码。
一、var:历史的包袱,必须淘汰的“糟粕”
为什么说 var 是“坏”的?
它存在两个致命问题:变量提升和函数作用域,导致代码逻辑混乱。
1. 变量提升(Hoisting)—— 编译阶段的“迷惑行为”
console.log(a); // 输出: undefined (不是报错!)
var a = 10;
- 编译阶段:
var声明被提升到作用域顶部(但只提升声明,不提升赋值)。 - 执行阶段:赋值才发生。
- 后果:代码逻辑“反直觉”,提前访问变量返回
undefined,容易引发 bug。
💡 举个栗子:
console.log(age); // undefined var age = 18;
2. 作用域问题—— 无块级作用域
if (true) {
var name = "Tom";
}
console.log(name); // "Tom" —— 本应只在 if 块内生效!
var作用域是函数级(Function Scope),而非块级(Block Scope)。- 在大型项目中,变量污染(变量意外覆盖)是高频问题。
✅ 结论:ES5 时代,
var是唯一选择;但 ES6 之后,它已成为历史包袱,务必淘汰。
二、let:ES6 的“块级作用域”救星
核心特性:块级作用域 + 暂时性死区(TDZ)
{
let height = 188;
console.log(height); // 188
}
console.log(height); // ReferenceError: height is not defined
为什么 let 更安全?
-
块级作用域
let声明的变量只在当前块({})内有效,彻底解决作用域污染问题。for (let i = 0; i < 5; i++) { // i 仅在 for 块内有效 } console.log(i); // ReferenceError: i is not defined -
暂时性死区(TDZ)
在声明前访问let变量,会立即报错!console.log(height); // ReferenceError: Cannot access 'height' before initialization let height = 188;- 编译阶段:变量被标记为“TDZ”(暂时性死区)。
- 执行阶段:只有到达
let height = 188;才激活。 - 作用:杜绝“变量提升”带来的逻辑陷阱。
💡 关键对比:
特性 varlet作用域 函数级 块级 变量提升 ✅(提升声明) ❌(TDZ 阻止提前访问) 重复声明 ✅(覆盖) ❌(SyntaxError)
三、const:常量的正确打开方式
核心特性:块级作用域 + 不可变引用
const PI = 3.1415926;
PI = 3.14; // TypeError: Assignment to constant variable.
重点: “常量” ≠ “不可变”
-
简单类型(
string,number,boolean):值不可变。const name = "Alice"; name = "Bob"; // TypeError -
复杂类型(
object,array):引用地址不可变,但内部属性可变。const person = { name: "Tom", age: 25 }; person.age = 26; // ✅ 允许! person = { name: "Jerry" }; // TypeError: Assignment to constant variable.
如何实现“完全冻结”?
const person = { name: "Tom", age: 25 };
const frozenPerson = Object.freeze(person); // 深度冻结对象
frozenPerson.age = 26; // ❌ 无效(严格模式下会报错)
💡 最佳实践:
- 优先用
const声明变量(除非需要修改)。- 用
Object.freeze确保对象不可变。
四、常见错误 & 解决方案(附代码)
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
ReferenceError: height is not defined | 作用域外访问 let/const | 检查变量声明位置,确保在作用域内 |
TypeError: Assignment to constant variable | 尝试修改 const 变量 | 改用 let 或检查是否误用 const |
ReferenceError: Cannot access 'PI' before initialization | 提前访问 let/const(TDZ) | 将变量声明移到使用位置之前 |
✅ 正确写法:
// 错误:提前访问 console.log(PI); // ❌ 报错 const PI = 3.14; // 正确:声明在前 const PI = 3.14; console.log(PI); // ✅ 3.14
五、为什么必须放弃 var?—— 现代 JavaScript 的最佳实践
-
var无处不在的坑- 作用域污染(
for循环中var i导致闭包问题)。 - 变量提升导致逻辑混乱(
console.log(a); var a = 1;)。
- 作用域污染(
-
let/const的优势- 可读性:变量作用域清晰,一眼定位。
- 安全性:TDZ 阻止“提前访问”,
const防止意外修改。 - 工程化:大型项目中减少 bug,提升协作效率。
🚫 淘汰
var的理由:“ES5 时代
var是唯一选择,但 ES6 之后,var只是历史的尘埃。—— 《JavaScript 语言精粹》”
六、结论:从现在开始尽量只用 let 和 const
| 场景 | 推荐用法 | 说明 |
|---|---|---|
| 变量值可能变化 | let | 例如:循环计数器、状态变量 |
| 常量(值永不变化) | const | 例如:PI、API 地址 |
| 需要完全不可变的对象 | const + Object.freeze | 例如:配置对象 |
✨
var→ 99% 的场景都该淘汰。const是默认选择,除非明确需要修改变量。let是const的补充,用于需要修改的场景。
附:ES6 语法对比速查表
| 语法 | 作用域 | 提升 | 重复声明 | 适用场景 |
|---|---|---|---|---|
var | 函数级 | ✅ | ✅ | ❌ 历史遗留,禁用 |
let | 块级 | ❌ | ❌ | 需要修改的变量 |
const | 块级 | ❌ | ❌ | 常量(值不变) |
“在 JavaScript 中,
let和const不是语法糖,而是语言设计的进化。用它们写代码,你会感觉像在用 Java/C++ 一样安全。” —— 《JavaScript 高级程序设计》