小白必看:《JavaScript 预编译揭秘》

123 阅读4分钟

预编译揭秘

上次作用域文章中我们提到声明提升,可是当我们面试时,光知道声明提升是不够的。当你的面试官想对你刨根问底时,理解预编译可以帮我们面对面试官的刨根问底,让我们的底层知识更加深入。

首先我们复习一下什么是声明提升

在JavaScript中,变量和函数声明会在代码实际执行之前被提升到它们所在作用域的顶部。这意味着无论变量或函数在代码中的位置如何,它们都会被视为在其作用域的顶部声明。具体来说:

  • var声明的变量会声明提升: 使用var关键字声明的变量会被提升,但它们的赋值不会提升。这意味着变量的声明会被移动到作用域的顶部,但它们的赋值将保持在原地。这会导致变量在声明前就可以被访问,其值为undefined
  • 函数声明整体提升: 使用函数声明方式创建的函数会整体被提升,包括函数名和函数体。这允许你在函数声明前调用函数,因为整个函数声明都被提升。

预编译发生在函数体内时

当预编译发生在函数体内时,以下步骤会依次执行:

  1. 创建函数的AO对象(Action Object): 在函数被调用时,会创建一个AO对象,它是函数执行上下文的一部分,用于存储函数内部的变量和函数。
  2. 找形参和变量声明: 预编译会找到函数内的形参(函数参数)和变量声明,并将它们作为AO对象的属性名,初始值赋予undefined
  3. 将形参和实参统一: 预编译会将传递给函数的实参与形参对应起来,以便在函数内使用。
  4. 在函数体内找函数声明: 如果函数内部有函数声明,它们也会被提升,并将函数名作为AO对象的属性名,值为对应的函数体。

这样,预编译确保了函数内部的变量和函数在执行时可以被正确访问,即使它们在代码中的位置位于函数调用之后。

分析如下代码:

// AO ={  预编译第一步,创建AO对象
//     a:undefined -> 1 -> 3,
//     b:undefined -> function b(){} -> 2,
//     c:undefined
//     d:function d(){}
// }

function foo(a,b){
    console.log(a); //1
    c = 0;
    var c;
    a = 3
    b = 2
    console.log(b);  //2
    function b(){}
    function d(){}
    console.log(b);  //2
}
foo(1)

找形参和变量声明: 形参a b 值为undefined,声明的变量 a。

将形参和实参统一: a 的值被覆盖为 1

在函数体内找函数声明: 声明了俩个函数 a b,它们的值为 function b(){} function d(){}。

接着开始执行,来到第19行 调用函数 进入函数内 得到 a 的值 1。 执行赋值语句c=0,a=3,b=2 打印b的值,得到2(*这里b有一点需要注意,之前我们说在js中,b=2 会溢出到全局作用域中,而这里仍然可以得到b的值为2,管你听没听懂,记就完了~!-.-)同理下面的打印语句得到的还是2.

预编译发生在全局

当预编译发生在全局作用域时,以下步骤会依次执行:

  1. 创建GO对象(Global Object): 在全局作用域中,会创建一个GO对象,用于存储全局变量和函数。
  2. 找变量声明: 预编译会找到全局作用域内的变量声明,并将它们作为GO对象的属性名,初始值赋予undefined
  3. 在全局找函数声明: 如果全局作用域内有函数声明,它们也会被提升,并将函数名作为GO对象的属性名,值为对应的函数体。

这保证了全局作用域内的变量和函数可以在整个程序中被访问,无论它们在代码中的位置如何。

// GO={创建GO对象
//     global:undefined -> 100, 
//     fn:function fn(){},
// }

// AO ={
//     global: undefined -> 200 -> 300,

// }
global = 100
function fn(){
    console.log(global);// undefined
    global = 200
    console.log(global);// 200
    var global = 300
}
fn()
var global

找变量声明: global

在全局找函数声明: fn

执行 将100赋值给global 调用函数fn 对函数进行编译

找形参和变量声明: global = undefined

将形参和实参统一:

在函数体内找函数声明:

执行 打印得到global的值undefined 将200赋值给global 打印global得到200 将300赋值给global。结束

总结一下,了解JavaScript中的声明提升和预编译过程对于正确理解代码执行顺序和作用域非常重要。变量声明和函数声明在不同情况下会被提升,确保它们在需要时可用。这个概念对于避免在代码中出现意外错误和理解JavaScript的工作原理非常关键。