理解 JavaScript 中“提升”的本质及其发生时机
掌握 var、let、const 在提升行为上的关键差异
了解函数声明与函数表达式的提升规则
掌握暂时性死区(Temporal Dead Zone, TDZ)的概念与影响
能够避免因提升导致的常见运行时错误
提升是 JavaScript 引擎在编译阶段将变量、函数和类的声明移动到其作用域顶部的行为。
注意:只有声明被提升,初始化(赋值)不会被提升!
这意味着,无论你在代码何处声明变量或函数,JavaScript 都会“提前知道”它们的存在——但能否安全使用,取决于声明方式。
Temporal Dead Zone, TDZ
在深入提升之前,必须理解 TDZ:
TDZ 是指从进入作用域开始,到变量实际初始化完成之间的这段时间。
在此期间,访问 let 或 const 声明的变量会抛出 ReferenceError。
var 没有 TDZ,它会被初始化为 undefined。
console.log(x); // ReferenceError: 初始化前无法访问 'x'
let x = 10;关键点:
let/const虽然也被“提升”,但在初始化前处于“不可访问”状态。
var 的提升声明提升 + 初始化为 undefined
console.log(a); // undefined
var a = 5;等效于:
var a; // 声明被提升
console ---> undefined
a = 5; // 赋值留在原地特点:
不会报错,但值为 undefined
允许重复声明(后声明覆盖前声明)
var a = 10;
var a = 20;
console.log(a); // 20let / const 的提升提升但处于 TDZ
console.log(b); // ReferenceError
let b = 10;尽管
b的声明被提升到块顶部,但在let b = ...执行前,它处于 TDZ,无法访问。
优势:防止在声明前意外使用变量,提高代码健壮性。
完整提升
greet(); // 正常输出:"Hello, Mahima!"
function greet() {
console.log("Hello, Mahima!");
}整个函数体(包括名称和实现)都被提升,可以在声明前调用。
仅变量名提升
hello(); // TypeError: hello is not a function
var hello = function() {
console.log("Hi!");
};等效于:
var hello; // 声明提升 → hello = undefined
hello(); // undefined() → TypeError
hello = function(){...}; // 赋值留在原地即使使用
let,也会因 TDZ 报错:
hi(); // ReferenceError
let hi = function() { };类声明也会被提升,但受 TDZ 限制:
const obj = new MyClass(); // ReferenceError
class MyClass {
constructor() {
this.name = "Mahima Bhardwaj";
}
}虽然
MyClass被提升,但在class语句执行前无法访问,这是为了防止在类定义前实例化。
function test() {
console.log(x); // ReferenceError(TDZ)
let x = 50;
}
test();
let x被提升到函数顶部,但仍在 TDZ 中,直到赋值语句执行。
function outer() {
console.log(a); // undefined(var 提升)
var a = 5;
function inner() {
console.log(b); // undefined(var 提升)
var b = 10;
}
inner();
}
outer();每个函数作用域独立进行提升。
var 问题经典闭包陷阱
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出:3, 3, 3原因:
var i被提升到函数 / 全局作用域,所有回调共享同一个i,循环结束后i === 3。
解决方案:使用 let(块级作用域):
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出:0, 1, 2
}test(10); // 10
function test(num) {
console.log(num);
}函数整体(含参数签名)被提升,参数值由调用时传入决定,与提升无关。
始终在作用域顶部声明变量,避免依赖提升逻辑。
优先使用 let 和 const,它们的 TDZ 机制能帮助你发现潜在错误。
避免使用 var,除非需要兼容老旧环境。
函数尽量使用声明式写法(function foo() {}),便于提升和可读性。
不要在声明前调用函数表达式或类。
以下代码会输出什么?为什么?
console.log(typeof foo); // ?
var foo = function() {};为什么下面的代码不会报错,而使用 let 会?
console.log(x);
var x = 1;如何修改以下代码,使其输出 0, 1, 2 而不是 3, 3, 3?(不使用 let)
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}