JS 跑代码前会先「提前翻一遍,把家伙事儿搬到门口」——这叫预解析。理解它,你才能解释为什么还没赋值就打印变量是 undefined、为什么函数能在定义前就调用。三个经典坑一次讲透。
① 预解析阶段:把当前范围里的 var 变量声明和 function 整台函数提前搬到最顶上;只搬「声明」,不搬「赋值和调用」。② 然后才从上往下真正执行。
console.log(num); // 打印出来是 undefined,没报错 var num = 10; // 预解析时其实相当于: // var num; ← 只把"声明"提前搬上来,盒子是空的 // console.log(num); // 这时候盒子还是空的 → undefined // num = 10; ← 走到这一行才往盒子里放10
如果你以为 let/const 也会被提前搬,就会搞混。因为 let/const 没有这种提升,它们有「暂时性死区」,声明前直接报错,不偷偷 undefined。
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 是函数内部的,外面没有
① 用在哪:解释「为什么声明前打印是 undefined」「为什么函数能提前调用」,面试常问。
② 常见坑:以为 let 也被提升;函数表达式提前调用报 not a function;连等 a=b=c=9 漏出全局。
③ 怎么解决:一律「先声明后使用」;不用 var 连等;看不懂输出就把代码按预解析规则手动还原一遍。