在 JavaScript 面试中,"let
、const
和 var
有什么区别?" 是一个高频基础题。看似简单,实则考察候选人对 变量声明机制、作用域、提升(hoisting)、暂时性死区(TDZ) 以及 ES6 新特性的理解深度。本文将从底层原理出发,全面剖析三者的异同,并结合实际代码示例,帮助你彻底掌握这一核心知识点。
一、作用域(Scope):最根本的区别
作用域决定了变量的可访问范围。这是三者最核心的差异。
声明方式 | 作用域类型 | 特点 |
---|---|---|
var |
函数作用域(Function Scope) | 在函数内部声明的 var 变量,只在该函数内可见;在全局声明则为全局变量。 |
let |
块级作用域(Block Scope) | 在 {} 内声明的 let 变量,只在该代码块内可见(如 if 、for 、while 等)。 |
const |
块级作用域(Block Scope) | 与 let 相同,具有块级作用域。 |
示例对比
javascript
// var - 函数作用域
function exampleVar() {
if (true) {
var x = 10;
}
console.log(x); // 输出 10,x 在整个函数内都可访问
}
exampleVar();
// let - 块级作用域
function exampleLet() {
if (true) {
let y = 20;
}
console.log(y); // 报错:y is not defined
}
exampleLet();
// const - 块级作用域
function exampleConst() {
if (true) {
const z = 30;
}
console.log(z); // 报错:z is not defined
}
exampleConst();
✅ 关键点 :
let
和const
的块级作用域解决了var
在循环中常见的闭包问题。
javascript
// 使用 var 的经典陷阱
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出 3, 3, 3
}
// 使用 let 的正确方式
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出 0, 1, 2
}
二、变量提升(Hoisting):声明被"提升",但初始化不会
JavaScript 引擎在执行代码前会进行"编译"阶段,将变量和函数声明提升到作用域顶部。
声明方式 | 是否提升 | 提升后状态 | 访问未声明前的变量 |
---|---|---|---|
var |
✅ 是 | 提升声明,初始化为 undefined |
可访问,值为 undefined |
let |
✅ 是 | 提升声明,但不初始化 | 报错:Cannot access before initialization |
const |
✅ 是 | 提升声明,但不初始化 | 报错:Cannot access before initialization |
示例说明
javascript
console.log(a); // undefined(var 被提升并初始化为 undefined)
var a = 1;
console.log(b); // 报错:Cannot access 'b' before initialization
let b = 2;
console.log(c); // 报错:Cannot access 'c' before initialization
const c = 3;
✅ 关键点 :
let
和const
虽然也被提升,但由于"暂时性死区(Temporal Dead Zone, TDZ)"的存在,无法在声明前访问,这比var
更安全。
三、暂时性死区(Temporal Dead Zone, TDZ)
TDZ 是 let
和 const
特有的概念。从进入作用域到变量被声明并初始化之前,该变量处于"暂时性死区"------任何访问都会抛出错误。
javascript
{
// TDZ 开始
console.log(d); // ReferenceError
let d; // TDZ 结束
console.log(d); // undefined
d = 4;
}
✅ 设计目的:避免因变量提升导致的"先使用后声明"的混乱,提高代码的可预测性和安全性。
四、重复声明(Redeclaration)
声明方式 | 同一作用域内是否允许重复声明 |
---|---|
var |
✅ 允许(后声明覆盖前声明) |
let |
❌ 不允许(报错) |
const |
❌ 不允许(报错) |
javascript
var x = 1;
var x = 2; // 合法
let y = 1;
let y = 2; // 报错:Identifier 'y' has already been declared
const z = 1;
const z = 2; // 报错:Identifier 'z' has already been declared
五、const
的"不可变性"真相
const
声明的变量不能重新赋值 ,但不等于其值不可变 。对于对象和数组,其引用不可变,但内容可变。
javascript
const obj = { name: 'Alice' };
obj.name = 'Bob'; // ✅ 合法,修改对象属性
obj.age = 25; // ✅ 合法,添加新属性
obj = { name: 'Charlie' }; // ❌ 报错:Assignment to constant variable
const arr = [1, 2];
arr.push(3); // ✅ 合法
arr[0] = 10; // ✅ 合法
arr = [4, 5]; // ❌ 报错
✅ 关键点 :
const
保证的是绑定(binding)的不可变性 ,而非值的不可变性。若需深度冻结对象,应使用Object.freeze()
。
六、全局对象属性绑定
在全局作用域中:
声明方式 | 是否成为全局对象(如 window )的属性 |
---|---|
var |
✅ 是 |
let |
❌ 否 |
const |
❌ 否 |
javascript
var a = 1;
let b = 2;
const c = 3;
console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined
✅ 意义 :
let
和const
避免了意外污染全局命名空间,更安全。
七、实际开发建议
- 优先使用
const
:如果你不打算重新赋值变量,就用const
。这是最安全的选择。 - 其次使用
let
:仅在需要重新赋值时使用let
。 - 避免使用
var
:除非兼容旧环境,否则应完全弃用var
。
javascript
// ✅ 推荐写法
const PI = 3.14159;
const user = { name: 'John' };
let count = 0;
for (let i = 0; i < 10; i++) {
count += i;
}
总结:一张表看懂所有区别
特性 | var |
let |
const |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | 是(初始化为 undefined ) |
是(不初始化,TDZ) | 是(不初始化,TDZ) |
暂时性死区 | ❌ 无 | ✅ 有 | ✅ 有 |
重复声明 | ✅ 允许 | ❌ 不允许 | ❌ 不允许 |
重新赋值 | ✅ 允许 | ✅ 允许 | ❌ 不允许 |
全局属性绑定 | ✅ 是 | ❌ 否 | ❌ 否 |
面试加分回答
"
let
和const
是 ES6 引入的块级作用域变量声明方式,它们解决了var
存在的变量提升副作用、作用域不清晰和全局污染等问题。const
提供了不可变绑定,鼓励函数式编程风格。在现代 JavaScript 开发中,应优先使用const
和let
,避免使用var
,以写出更安全、可维护的代码。"
掌握这些细节,你不仅能回答面试题,更能理解 JavaScript 的演进逻辑和最佳实践。