预解析就是JS 执行前先把 var 和 function 声明"搬"到最前面。
核心规则:
① var 声明提升——只提升声明不提升赋值,所以访问得 undefined
② function 整体提升——函数声明可以先调用后定义
③ let/const 不提升——暂时性死区,访问报错
坑:var 声明前访问不报错得 undefined、let 声明前访问报 ReferenceError、函数表达式不会提升。
① 预解析阶段:把当前范围里的 var 变量声明和 function 整台函数提前搬到最顶上;只搬「声明」,不搬「赋值和调用」。② 然后才从上往下真正执行。
// 打印出来是 undefined,没报错 console.log(num); // 预解析时其实相当于: let num = 10;
如果你以为 let/const 也会被提前搬,就会搞混。因为 let/const 没有这种提升,它们有「暂时性死区」,声明前直接报错,不偷偷 undefined。
// 还没写定义就喊,居然也行
fn();
// 因为整台 function 被提前装好了
function fn() {
console.log("打印");
}
// 报错:fn2 is not a function
fn2();
let fn2 = function () {
console.log("想不到吧");
// 预解析只搬了 let fn2 这个空盒子
};
陷阱1:函数里写了 var,会盖住外面同名的。函数里只要出现 let num,整个函数内的 num 都用这个自己的,哪怕写在打印之后:
// 全局 num 是10
let num = 10;
fun();
function fun() {
// 打印 undefined,不是10
console.log(num);
// 函数里有 let num → 整个函数都用这个,盖住全局
let num = 20;
}
陷阱2:不写 var 直接赋值,东西漏成了全局。尤其是连等:
function f1() {
// 其实拆开是:let a=9; b=9; c=9;
let a = b = c = 9;
// 只有 a 有 var,b、c 没写 var → 漏成了全局!
}
f1();
// 9 9(外面也能取到 b、c)
console.log(b, c);
// 报错:a 是函数内部的,外面没有
console.log(a);
① 用在哪:解释「为什么声明前打印是 undefined」「为什么函数能提前调用」,面试常问。
② 常见坑:以为 let 也被提升;函数表达式提前调用报 not a function;连等 a=b=c=9 漏出全局。
③ 怎么解决:一律「先声明后使用」;不用 var 连等;看不懂输出就把代码按预解析规则手动还原一遍。
| API | 作用 | 参数 | 返回值 | 代码示例 |
|---|---|---|---|---|
| var 提升 | 声明被提前,赋值不提前 | 变量名 | undefined | console.log(x); let x = 1; // 打印 undefined |
| 函数声明提升 | 整个函数提前,可先调用 | 函数名 | 可调用 | f();
function f() {}
// 不报错 |
| let 不提升 | 声明前用会报错(TDZ) | 变量名 | 报错 | console.log(x); let x = 1; // 直接报错 |