一文搞懂 JavaScript 预编译原理

0 阅读3分钟

在了解js预编译之前,我们要先知道 v8 引擎是怎么工作的,以及js的作用域工作原理:[具体见](url juejin.cn/post/763892…)

v8 引擎是怎么工作的

1.分词
2.语法分析得到 AST 树
3.代码生成

预编译

首先我们要知道编译发生在代码执行之前,因此js这门编程语言中就出现了声明提升这一特性。

console.log(a);
var a = 1;

在js中这份代码并不会报错,而是输出 undefined。这就是因为声明提升,在编译时代码会变成

var a;
console.log(a);
a = 1;

那么在知道js有声明提升这个概念后,我们就想知道它为什么会出现声明提升,这就是预编译带来的结果。

预编译分为函数体内的预编译全局的预编译

函数体内的预编译

函数体内的预编译分为四步:
1.创建一个执行上下文 AO:{}
2.找形参和变量声明,将形参和变量名作为属性名,添加到 AO 中,值为 undefined
3.将形参和实参统一
4.在函数体内找函数声明,将函数名作为 AO 中的属性名,函数体作为属性值

function fn(a){
    console.log(a);  
    var a = 123;
    console.log(a); 
    function a(){};
    var b = function(){};
    console.log(b); 
    function c(){};
    var c = a;
    console.log(c); 
}
fn(1);

例如上面这份代码,它的预编译过程就为:
创建一个对象

AO = {
}

然后找形参和变量声明,代码中的形参为 a,变量声明有 var a , var b , var c ,将这些添加到 AO 中,值为 undefined

AO = {
    a: undefined,
    b: undefined,
    c: undefined
}// 如果属性名相同,那么用后面值的覆盖前面的值

接着将形参和实参统一

AO = {
    a: 1,
    b: undefined,
    c: undefined
}

最后在函数体内找函数声明,函数声明有 function a(){}function c(){} ,然后将函数名作为 AO 中的属性名,函数体作为属性值

AO = {
    a: function(){},
    b: undefined,
    c: function(){}
}

这就是函数体内的预编译后的结果。

然后就是代码执行

function fn(a){
    console.log(a); // function(){} 
    var a = 123
    console.log(a); // 123
    function a(){}
    var b = function(){}
    console.log(b); // function(){} 
    function c(){}
    var c = a
    console.log(c); // 123
}

执行结果为

image.png

全局的预编译

全局的预编译分为三步:

  1. 创建全局执行上下文 GO:{}
  2. 找全局变量声明,将全局变量名作为属性名,添加到 GO 中,值为 undefined
  3. 找全局函数声明,将函数名作为 GO 的属性名,函数体作为属性值
var a ;
var b = 2;
function a () {
  console.log(a);
  var c = 3;
  var a = b;
  function c() {};
  console.log(c); 
}
a();
console.log(a);

以上面这个代码为例: 首先创建 GO:{}

GO = {
}

然后找全局变量,有 var avar b ,添加到 GO 中,值为 undefined

GO = {
    a: undefined,
    b: undefined
}

最后找全局函数声明,有 function a(){} ,添加到 GO

GO = {
    a: function(){},
    b: undefined
}

这就是全局的预编译。 然后就是执行,执行过程中遇到 a() ,即调用了 函数体 a ,接着就进行 函数体内的预编译

AO = {
    c: function(){},
    a: undefined
}

然后继续执行

var a ;
var b = 2;
function a () {
  console.log(a); // undefined
  var c = 3;
  var a = b;
  function c() {};
  console.log(c);  // 3
}
a();
console.log(a); // function(){}

最后运行结果为:

屏幕截图 2026-05-14 233409.png

总结

JavaScript 在执行代码之前会进行预编译,将变量和函数声明提升到作用域顶部。函数体预编译创建 AO 对象,全局预编译创建 GO 对象,这一机制正是 JavaScript「声明提升」现象的根源。