在 JavaScript 中,Hoisting(提升) 是一个非常重要但又容易引起误解的概念。它指的是:
在代码执行之前,JavaScript 引擎会对代码进行预处理,
将变量声明和函数声明“提升”到当前作用域的顶部。
这意味着可以在变量或函数声明之前访问它们,但不同的声明方式(var、let、const、函数声明、函数表达式)表现并不相同。
1. Hoisting 的本质
虽然 JavaScript 是一种“解释型语言”,但在执行之前,它实际上会经历一个 预编译阶段(创建阶段):
- 扫描代码:识别所有的变量声明和函数声明;
- 注册声明:将它们放入当前作用域的内存空间;
- 开始执行:随后逐行执行程序逻辑。
这种“先收集声明、后执行语句”的机制,正是“变量提升”现象的本质。
JavaScript 的执行过程可分为两个阶段:
- 编译阶段(创建阶段):创建执行上下文(Execution Context),并将变量和函数声明放入内存;
- 执行阶段:逐行执行代码逻辑。
📌 注意:
在编译阶段,
- 变量声明 与 函数声明 会被“提升”;
- 但 变量赋值不会被提升。
2. var 的变量提升
console.log(a); // 输出:undefined
var a = 10;
引擎实际执行等价于:
var a; // 声明被提升
console.log(a); // undefined
a = 10; // 赋值
🧩 解释:
var声明被提升;- 赋值不会被提升;
- 因此在声明前访问变量不会报错,但结果是
undefined。
3. 函数声明的整体提升
foo(); // ✅ 正常执行
function foo() {
console.log("Hello");
}
执行前,引擎会将函数声明整体提升(包括函数体):
function foo() {
console.log("Hello");
}
foo(); // 调用
🧠 结论:函数声明会被整体提升,因此可以在定义之前安全调用。
4. 函数表达式不会被整体提升
foo(); // ❌ TypeError: foo is not a function
var foo = function() {
console.log("Hello");
};
解释:
var foo的声明被提升;- 但函数表达式的赋值不会;
- 因此在执行阶段
foo仍为undefined,调用时会报错。
5. let 与 const 的暂时性死区
console.log(a); // ❌ ReferenceError: Cannot access 'a' before initialization
let a = 10;
虽然 let 和 const 在技术上也会被提升到作用域顶部,但在声明之前的区域内不可访问。
这段区域被称为:
⚠️ 暂时性死区(Temporal Dead Zone, TDZ)
📌 因此:
- 在 TDZ 内访问变量会抛出错误;
- 直到执行到声明语句时,变量才可安全使用。
6. 变量提升的优势
尽管现代开发更推荐使用 let 和 const,但在早期 JavaScript 设计中,变量提升确实提供了一些便利性。
✅ 优势 1:允许函数“先调用后定义”
sayHello(); // ✅ 输出“你好”
function sayHello() {
console.log("你好");
}
函数声明被整体提升,使得逻辑可以先写主流程,再定义细节。
📘 类似于 C 语言的“函数原型声明”,让无类型系统的 JS 也能“先用后定义”。
✅ 优势 2:减少“未定义变量”报错(针对 var)
if (flag) {
var msg = "Hello";
}
console.log(msg); // undefined
var 被提升后,不会立刻报错,而是 undefined。
这在早期浏览器脚本中提升了容错性,使脚本运行更稳定。
✅ 优势 3:代码结构更灵活
function demo() {
console.log(a); // undefined
var a = 10;
console.log(a); // 10
}
demo();
早期脚本允许开发者灵活组织代码顺序,声明可放在逻辑中间,仍能正常运行。
7. 变量提升的缺点
随着项目规模增大,Hoisting 的弊端逐渐显现。
❌ 缺点 1:降低代码可读性
console.log(a); // undefined
var a = 10;
在声明前使用变量虽然不会报错,但行为不直观,容易让人误判执行逻辑。
❌ 缺点 2:变量覆盖与作用域混乱
var count = 5;
function demo() {
if (true) {
var count = 10; // 在同一作用域中重新声明
}
console.log(count); // 10
}
demo();
var 没有块级作用域,变量容易被覆盖或意外共享,调试困难。
❌ 缺点 3:增加调试难度
console.log(msg); // undefined
if (false) {
var msg = "Hello";
}
虽然逻辑上看似“未声明”,但变量实际上已被提升为 undefined,容易让人误判问题根源。
8. 现代解决方案
为了解决提升带来的混乱,ES6 引入了更安全、清晰的变量机制:
let和const:具备暂时性死区(TDZ),防止访问未声明变量;- 块级作用域(Block Scope):防止变量污染全局空间;
- 更明确的声明时机:代码结构更直观、可预测。
✅ 现代开发建议:
- 继续使用函数提升(function declaration),方便组织逻辑;
- 避免使用
var,推荐统一使用let/const; - 保持声明靠近使用处,提升可读性与可维护性。
698

被折叠的 条评论
为什么被折叠?



