var、let 和 const
ES6 之前只有 var 一种声明变量的方式,它存在一些容易导致 bug 的”特性”。ES6 引入 let 和 const 正是为了解决这些问题。
var 的历史包袱
1. 函数级作用域
var 只有函数作用域,没有块级作用域。在 if、for 等代码块中声明的变量会穿透到外部。
if (true) {
var name = "hello";
}
console.log(name); // "hello" — 块外仍可访问经典问题:循环中的异步回调拿到的始终是循环结束后的值。
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// 输出:3, 3, 32. 变量提升(Hoisting)
var 声明会被提升到函数顶部,但赋值保留在原位,导致拿到 undefined 也不报错。
function varExample() {
console.log(a); // 提升,值为undefined
var a = 10;
console.log(a); // 赋值后,值为10
if (true) {
var a = 20; // 覆盖了外层 a
console.log(a); // 输出20
}
console.log(a); // 输出20
}3. 允许重复声明
同一作用域内重复声明同名变量不会报错,容易覆盖已有值。
var x = 1;
var x = 2; // 静默通过4. 挂载到全局对象
在顶层使用 var 声明的变量会成为 window(或 globalThis)的属性,可能污染全局命名空间。
var globalVar = 1;
console.log(window.globalVar); // 1let / const 如何解决上述问题
1. 块级作用域
let 和 const 以 {} 为作用域边界,块外不可访问。
if (true) {
let name = "hello";
}
console.log(name); // ReferenceError: name is not defined循环中的 let 在每次迭代都会创建一个新的绑定,异步回调能拿到正确的值。
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// 输出:0, 1, 22. 禁止重复声明
同一作用域内 let / const 不允许重复声明同名变量,直接报错,更早暴露问题。
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared3. 不挂载到全局对象
顶层 let / const 不会成为 window 的属性,避免了全局污染。
let notGlobal = 1;
console.log(window.notGlobal); // undefinedconst 的独有特性
const 必须在声明时初始化,且不能重新赋值。但注意:它锁定的是变量引用,对于对象类型,内部属性仍然可变。
const obj = { a: 1 };
obj.a = 2; // 允许
obj = { b: 3 }; // TypeError: Assignment to constant variable如果希望对象属性也不可变,需要使用 Object.freeze()。
暂时性死区(TDZ)
可以简单理解为:声明前无法访问 let 和 const 同样存在提升行为,但在声明之前存在暂时性死区——此期间访问变量会抛出
ReferenceError,而非像 var 那样返回 undefined。
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 1;TDZ 从块作用域起点开始,到变量声明语句结束。这使得代码行为更可预测,强迫先声明后使用。
总结
var | let | const | |
|---|---|---|---|
| 作用域 | 函数级 | 块级 | 块级 |
| 重复声明 | 允许 | 禁止 | 禁止 |
| 变量提升 | 有(初始化为 undefined) | 有(TDZ) | 有(TDZ) |
| 全局属性 | 是 | 否 | 否 |
| 可重新赋值 | 是 | 是 | 否 |
默认使用
const,只有确定变量需要重新赋值时才用let,避免使用var。
面试题总结
- 请谈谈var、let、const三者的区别
- 作用域不同,var:函数作用域;const,let:块级作用域。 1.1. 解决了if/for中变量泄露问题 1.2. 不会影响或污染全局变量
- var:变量提升(Hoisting);let,const:TDZ暂时性死区
2.1
var声明前访问值为undefined 2.2 之后增加了let/const直接抛出ReferenceError - let,const:不允许重复声明
- 对于const来说,对于引用型变量来说,例如数组,Map等,只保持对引用的不变,而这些对象的内部数据是可以发生改变的。