← 返回目录

9. 预解析与变量函数提升

JS 跑代码前会先「提前翻一遍,把家伙事儿搬到门口」——这叫预解析。理解它,你才能解释为什么还没赋值就打印变量是 undefined、为什么函数能在定义前就调用。三个经典坑一次讲透。

9.1 互动演示(预测输出 + 预解析等价还原)

先自己猜输出,再点按钮看答案。

9.2 知识点逐条讲解(配合代码)

① 两步走:先搬家伙,再从上往下跑

① 预解析阶段:把当前范围里的 var 变量声明function 整台函数提前搬到最顶上;只搬「声明」,不搬「赋值和调用」。② 然后才从上往下真正执行。

② var 提升:只搬空盒子,不搬里面的东西

console.log(num);   // 打印出来是 undefined,没报错
var num = 10;
// 预解析时其实相当于:
// var num;          ← 只把"声明"提前搬上来,盒子是空的
// console.log(num); // 这时候盒子还是空的 → undefined
// num = 10;         ← 走到这一行才往盒子里放10

	

如果你以为 let/const 也会被提前搬,就会搞混。因为 let/const 没有这种提升,它们有「暂时性死区」,声明前直接报错,不偷偷 undefined

③ function 整台提升:可以先调用后定义

fn();              // 还没写定义就喊,居然也行
function fn() {    // 因为整台 function 被提前装好了
    console.log("打印");
}

	

④ 函数表达式提升陷阱:只搬了个空变量名

fn2();             // 报错:fn2 is not a function
var fn2 = function () {
    console.log("想不到吧");
};
// 预解析只搬了 var fn2 这个空盒子
// 函数本体没搬,真正执行到时 fn2 还是 undefined
// undefined 不能加括号喊,所以报错

	

⑤ 两大陷阱

陷阱1:函数里写了 var,会盖住外面同名的。函数里只要出现 var num,整个函数内的 num 都用这个自己的,哪怕写在打印之后:

var num = 10;          // 全局 num 是10
fun();
function fun() {
    console.log(num);  // 打印 undefined,不是10
    var num = 20;      // 函数里有 var num → 整个函数都用这个,盖住全局
}

	

陷阱2:不写 var 直接赋值,东西漏成了全局。尤其是连等:

function f1() {
    var a = b = c = 9;
    // 其实拆开是:var a=9; b=9; c=9;
    // 只有 a 有 var,b、c 没写 var → 漏成了全局!
}
f1();
console.log(b, c);   // 9 9(外面也能取到 b、c)
console.log(a);      // 报错:a 是函数内部的,外面没有

	

⑥ X.3 实战:用在哪 / 常见坑 / 怎么解决

① 用在哪:解释「为什么声明前打印是 undefined」「为什么函数能提前调用」,面试常问。

② 常见坑:以为 let 也被提升;函数表达式提前调用报 not a function;连等 a=b=c=9 漏出全局。

③ 怎么解决:一律「先声明后使用」;不用 var 连等;看不懂输出就把代码按预解析规则手动还原一遍。

一句话记住:预解析=先搬声明再执行;var 只搬空盒子不搬赋值(提前用是 undefined);function 整台搬;函数表达式只搬变量名(提前喊报错);函数内 var 盖住同名;不写 var 的连等会漏成全局