JavaScript 变量声明三巨头:var、let、const 彻底搞懂
标签:#JavaScript #前端 #变量声明 #作用域 #暂时性死区
在 JavaScript 中,变量声明看似简单,但 var、let、const 的行为却大不相同。很多初学者甚至工作多年的开发者,都曾被它们的“变量提升”、“暂时性死区”、“作用域”等问题困扰。
今天,我们就来彻底搞懂这三种变量声明方式的区别,帮你避开常见坑点,写出更安全、可维护的代码。
一、JavaScript 中如何声明变量?
在 JS 中,我们有三种方式声明变量:
js
编辑
var name = "张三";
let age = 20;
const PI = 3.14;
虽然都能“声明变量”,但它们的作用域、提升行为、可修改性完全不同。
二、var:被时代淘汰的“老将”
✅ 基本用法
js
编辑
var a = 1;
❌ 三大“反直觉”问题
1. 变量提升(Hoisting)
var 声明的变量会被“提升”到作用域顶部。
js
编辑
console.log(a); // undefined,而不是报错
var a = 1;
👉 原理:在编译阶段,JS 引擎会把 var a; 提升到顶部,但赋值留在原地。
js
编辑
// 实际执行顺序
var a;
console.log(a); // undefined
a = 1;
2. 函数作用域(Function Scope)
var 只有函数作用域,没有块级作用域。
js
编辑
{
var age = 18;
}
console.log(age); // 18!可以访问
👉 即使在 {} 块中声明,var 也会“逃逸”到外层作用域。
3. 可重复声明
js
编辑
var name = "张三";
var name = "李四"; // 不报错!
👉 这容易导致变量覆盖,引发 bug。
三、let:现代 JS 的推荐选择
✅ 基本用法
js
编辑
let name = "张三";
✅ 核心特性
1. 块级作用域(Block Scope)
js
编辑
{
let age = 18;
}
console.log(age); // ❌ 报错:ReferenceError: age is not defined
👉 let 只在 {} 块内有效,出了块就“消失”。
2. 暂时性死区(Temporal Dead Zone, TDZ)
在声明之前访问 let 变量会报错!
js
编辑
console.log(name); // ❌ 报错:Cannot access 'name' before initialization
let name = "张三";
👉 这比 var 的 undefined 更安全,避免了“静默错误”。
3. 不可重复声明
js
编辑
let name = "张三";
let name = "李四"; // ❌ 报错:Identifier 'name' has already been declared
四、const:常量声明,但不是“不可变”
✅ 基本用法
js
编辑
const PI = 3.14;
✅ 核心规则
const只保证“指向不变”,不保证“内容不变”
1. 基本类型:不能修改
js
编辑
const age = 18;
age = 20; // ❌ 报错:Assignment to constant variable.
2. 对象/数组:可以修改内容
js
编辑
const person = {
name: "张三",
age: 18
};
person.age = 20; // ✅ 合法
person.name = "李四"; // ✅ 合法
person = {}; // ❌ 报错:不能重新赋值
🔥 重点:
const锁的是“变量的指向”,而不是“对象的内容”。
3. 如何让对象真正不可变?
使用 Object.freeze():
js
编辑
const frozenPerson = Object.freeze({
name: "张三",
age: 18
});
frozenPerson.age = 20; // ❌ 静默失败(严格模式下报错)
Object.freeze():可以冻结对象,使其真正不可变
五、常见报错解析
❌ ReferenceError: height is not defined
-
原因:变量未声明,或在作用域外访问。
-
示例:
js 编辑 { let height = 180; } console.log(height); // 报错
❌ TypeError: Assignment to constant variable.
-
原因:试图给
const变量重新赋值。 -
示例:
js 编辑 const name = "张三"; name = "李四"; // 报错
❌ ReferenceError: Cannot access 'PI' before initialization
-
原因:在
let或const声明前访问,处于“暂时性死区”。 -
示例:
js 编辑 console.log(PI); const PI = 3.14; // 报错
六、最佳实践建议
| 场景 | 推荐写法 |
|---|---|
| 声明变量 | 优先使用 let |
| 声明常量(如配置、数学常数) | 使用 const |
避免 var | 除非兼容老环境 |
| 修改对象 | 用 ... 扩展运算符创建新对象,避免直接修改 |
| 数组操作 | 用 map、filter 等函数式方法,避免 push 直接修改 |
✅ 推荐代码风格
js
编辑
// ✅ 好的写法
const API_URL = "https://api.example.com";
let userCount = 0;
function updateUser() {
userCount += 1;
const newUser = { ...user, lastLogin: Date.now() };
return newUser;
}
七、总结对比表
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | ✅(值为 undefined) | ✅(但有 TDZ) | ✅(但有 TDZ) |
| 暂时性死区 | ❌ | ✅ | ✅ |
| 可重复声明 | ✅ | ❌ | ❌ |
| 可重新赋值 | ✅ | ✅ | ❌ |
| 推荐使用 | ❌ | ✅ | ✅ |
✅ 结语
var是“历史遗留”,行为反直觉,不推荐使用。let是“现代标准”,块级作用域,安全可靠,推荐用于变量声明。const是“最佳实践”,明确表示“不可重新赋值”,推荐用于常量和对象声明。
记住一句话:
const优先,let次之,var不用。
掌握这三者的区别,不仅能避免常见 bug,还能写出更清晰、可维护的代码。