一、什么是提升?
提升(Hoisting)是 JavaScript 引擎的一种特性:在代码执行前,JavaScript 引擎会将变量和函数的声明提升到其作用域的顶部。这种机制允许在声明前使用某些变量或函数,但具体行为因语法结构而异。
二、提升的类型:完全提升 vs 不完全提升
2.1 完全提升
完全提升特指 普通函数声明 的提升行为,其核心特征包括:
声明部分:(函数名)被提升。
定义部分:(函数体)也被提升。
可以在声明前直接调用函数并执行其功能。
简言之:函数的 声明、定义和初始化 会被同时提升到作用域顶部,因此可以在声明前直接调用并执行。
代码示例
// 完全提升:普通函数声明
hello(); // 可以直接调用,输出 "Hello from normal function"
function hello() {
console.log('Hello from normal function');
}执行顺序(引擎实际处理过程):
提升
function hello() { console.log('Hello from normal function'); }到作用域顶部。执行
hello();→ 正常调用函数。执行函数声明(已提升,实际被忽略)
2.2 不完全提升
不完全提升 指的是:只有 声明部分 被提升,定义/赋值/初始化 部分留在原地,因此在声明前无法正常使用。
不完全提升又可细分为两种情况:
无暂时死区:如 var 变量。
有暂时死区:如 let 、const 和 类声明。
三、各类语法结构的提升行为
3.1 var 变量声明(不完全提升,无暂时死区)
// var 提升但未赋值
console.log(x); // 输出 undefined(声明被提升,赋值留在原地)
var x = 5;执行顺序:
提升 var x; 到作用域顶部(默认值 undefined )。
执行 console.log(x); → 输出 undefined。
执行 x = 5; → 完成赋值。
3.2 let 变量声明(不完全提升,有暂时死区)
// let 提升但处于暂时死区
// console.log(x); // 抛出 ReferenceError
// typeof x; // 同样抛出 ReferenceError
let x = 5;执行顺序:
提升 let x; 到作用域顶部(进入暂时死区,未初始化)。
执行 console.log(x); → 抛出 ReferenceError。
执行 let x = 5; 完成初始化和赋值。
3.3 const 常量声明(不完全提升,有暂时死区)
// const 提升但处于暂时死区
// console.log(y); // 抛出 ReferenceError
const y = 10;执行顺序:
提升 const y; 到作用域顶部(进入暂时死区,未初始化).
执行 console.log(y); → 抛出 ReferenceError。
执行 const y = 10; → 完成初始化和赋值。
3.4. 类声明(不完全提升,有暂时死区)
// 类提升但处于暂时死区
// const person = new Person(); // 抛出 ReferenceError
class Person {
constructor() {
this.name = "Alice";
}
}执行顺序:
提升 class Person; 到作用域顶部(进入暂时死区,未初始化)。
执行 const person = new Person(); → 抛出 ReferenceError。
执行 class Person { ... } → 完成类定义。
3.5 类内部的方法(无提升)
类内部的方法 完全不会被单独提升,它们的可用性依赖于类的初始化顺序:
3.5.1 原型方法(普通方法声明)
class Person {
constructor() {
this.sayHello(); // 可以正常调用(原型方法已添加到原型)
}
sayHello() { // 原型方法
console.log("Hello from prototype method");
}
}关键机制:
类定义时,所有原型方法(如 sayHello)被添加到类的原型上。
实例化时,先创建对象并建立原型链,再执行构造函数。
因此,构造函数中可以通过 this 访问所有原型方法。
3.5.2 实例字段方法(箭头函数)
class Person {
constructor() {
this.sayGoodbye(); // 可以正常调用(实例字段已初始化)
}
sayGoodbye = () => { // 实例字段方法
console.log("Goodbye from arrow function");
}
}关键机制:
实例化时,先初始化所有实例字段(包括箭头函数)。
再执行构造函数。
因此,构造函数中可以通过 this 访问所有实例字段方法。
四、完全提升 vs 不完全提升对比表
根据 JavaScript 规范,所有声明(包括 var、let、const、function、class)都会在代码执行前被提升到作用域顶部。但不同声明方式的提升行为有明显区别:

五、暂时死区(Temporal Dead Zone, TDZ)
暂时死区是 ES6 引入的概念:变量从声明开始到初始化完成前的区域,在此区域内访问变量会抛出 ReferenceError。
let/const 的暂时死区示例
function testTDZ() {
console.log("Start of function");
// TDZ 开始:let x 声明被提升
// console.log(x); // 抛出 ReferenceError
let x = 5; // TDZ 结束:初始化完成
console.log(x); // 输出 5
}六、类内部方法的特殊行为
类内部的方法(无论是原型方法还是箭头函数)没有提升行为,但:
原型方法:在类定义时添加到原型,构造函数执行时可通过原型链访问。
箭头函数:在实例化时初始化,构造函数执行前已可用。
关键结论
类内部方法的 声明顺序不影响构造函数中的调用 ,这不是因为提升,而是因为类的初始化机制。
七、最佳实践
总是在作用域顶部声明变量和函数,避免依赖提升机制。
优先使用 let / const, 而非 var,利用暂时死区避免意外错误。
先定义类,再使用类,不要在类定义前创建实例。
总结
JavaScript 的提升机制是其核心特性之一,但不同语法结构的提升行为差异较大:
只有普通函数声明是 完全提升。
var 、let、const和类声明都是 不完全提升。
类内部方法 没有提升,但其可用性依赖于类的初始化顺序。
理解这些差异有助于编写更安全、更可预测的 JavaScript 代码。