摘要
本文系统讲解 JavaScript 变量提升机制、暂时性死区(TDZ)以及 ES6 块级作用域,通过大量可运行案例清晰展示 var/let/const 在声明、提升、作用域上的差异,总结日常开发中的常见陷阱与最佳实践,帮助开发者从底层理解变量行为,避免隐性 bug,夯实前端基础。
一、变量提升:JavaScript 先 "扫描" 再执行
在 JS 执行代码前,引擎会先遍历当前作用域,把变量声明和函数声明 提升到作用域顶部,这一机制称为变量提升(Hoisting)。
关键点:
- 只提升声明,不提升赋值
- 函数声明整体提升,可在定义前调用
- var 变量提升为
undefined
1. var 变量提升
javascript
运行
javascript
console.log(name); // undefined
var name = "前端开发";
// 引擎实际执行顺序
var name;
console.log(name);
name = "前端开发";
2. 函数声明提升
javascript
运行
javascript
foo(); // 正常执行:hello
function foo() {
console.log("hello");
}
3. 函数表达式不会提升
javascript
运行
javascript
foo(); // TypeError: foo is not a function
var foo = function () {
console.log("hello");
};
4. 函数提升优先级高于变量
同名情况下,函数声明会覆盖 var 声明:
javascript
运行
javascript
console.log(foo); // [Function: foo]
var foo = 123;
function foo() {}
二、let /const 也会提升,但存在暂时性死区
很多人误以为 let/const 不提升,实际上它们同样提升 ,只是进入暂时性死区(TDZ),无法提前访问。
javascript
运行
javascript
console.log(age);
// Uncaught ReferenceError: Cannot access 'age' before initialization
let age = 20;
什么是暂时性死区 TDZ?
从作用域开始,到变量声明语句之间的区域,就是该变量的暂时性死区。在死区内访问变量会直接报错。
javascript
运行
javascript
{
// 此处开始到 let num 之前都是 TDZ
console.log(num); // 报错
let num = 10;
// TDZ 结束
}
经典陷阱:
javascript
运行
javascript
let a = 1;
{
console.log(a); // 报错!受内部 let 影响进入 TDZ
let a = 2;
}
三、ES6 块级作用域:{} 就是一个作用域
ES6 之前只有全局作用域 和函数作用域 。ES6 新增块级作用域 ,由 {} 包裹,if / for / while / try 都会形成块级作用域。
let /const 具有块级作用域
javascript
运行
javascript
{
let name = "张三";
const age = 20;
}
console.log(name); // ReferenceError
var 不受块级作用域限制
javascript
运行
javascript
{
var num = 100;
}
console.log(num); // 100,正常访问
典型场景:循环事件绑定问题
javascript
运行
javascript
// var 会泄露,输出都是 5
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0);
}
// let 具有块级作用域,输出 0 1 2 3 4
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0);
}
四、var /let/const 全方位对比
表格
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数 / 全局 | 块级 | 块级 |
| 变量提升 | 是,值为 undefined | 是,但 TDZ 不可访问 | 是,但 TDZ 不可访问 |
| 重复声明 | 允许 | 不允许 | 不允许 |
| 重新赋值 | 支持 | 支持 | 不支持 |
| 初始赋值 | 非必须 | 非必须 | 必须 |
| 挂载到 window | 是 | 否 | 否 |
| 开发推荐 | 不推荐 | 可变值使用 | 优先使用 |
五、常见面试题
-
**变量提升是什么?为什么会出现?**答:JS 执行前会扫描作用域并提升声明,只提升声明不提升赋值,目的是为了让函数可以在定义前调用。
-
**let 存在变量提升吗?**答:存在提升,但受暂时性死区限制,声明前访问会抛出引用错误。
-
**const 定义的对象可以修改属性吗?**答:可以修改属性与成员,不能修改引用(重新赋值)。
-
**暂时性死区的意义是什么?**答:强制规范变量先声明后使用,减少隐性错误,提升代码健壮性。
-
**为什么现代项目推荐 const > let > var?**答:const 语义明确、不可变、更安全;let 避免变量污染;var 易产生提升与全局污染问题。
六、总结
- 变量提升是 JS 执行前的声明扫描机制,只提升声明不提升赋值。
- 函数声明提升优先级高于 var 变量声明。
- let/const 存在提升,但受暂时性死区 TDZ 保护。
- let/const 支持块级作用域,可有效避免变量污染与循环问题。
- 现代开发规范:优先使用 const,需要变化时使用 let,尽量不使用 var。