JavaScript 预编译机制详解:从执行三部曲到 AO/GO 对象创建

作者:拾光时间:2026-10-24 08:26:41浏览:290

本文详细解析 JavaScript 代码执行的三个步骤:语法分析、预编译与解释执行。重点阐述函数执行前的预编译过程,包括创建 AO 对象、处理形参与变量声明、实参赋值及函数声明提升。同时对比全局作用域下的 GO 对象创建规则,并通过具体代码示例展示变量与函数在预编译阶段的行为差异,帮助开发者深入理解变量提升与作用域机制。

JavaScript 引擎在执行脚本时,遵循特定的工作流程。

整个执行过程可以划分为三个主要阶段。

  • 第一阶段是语法分析。
  • 第二阶段是预编译。
  • 第三阶段是解释执行。

JavaScript 引擎在处理代码时,首先会进行全面的语法检查,确保没有低级错误;随后进入预编译阶段,构建内部逻辑结构;最后才开始逐行解释并执行代码。

语法分析阶段

在代码实际运行之前,系统会扫描整个代码库,检测是否存在诸如括号缺失等基础语法错误。

预编译阶段

预编译操作发生在函数即将执行的前一刻。

如果变量在未经声明的情况下直接赋值,该变量会被挂载到全局对象上。

a = 3

var b = c = 4

所有在全局作用域中声明的变量,都会成为全局对象(如浏览器中的 window)的属性。

var a = 1 ===> window.a = 1

预编译的具体步骤可分为四步。

  1. 第一步:创建执行期上下文对象(AO,Activation Object)。该对象用于存储函数内部的局部变量。
  2. 第二步:查找形参和变量声明。将形参名和变量名作为 AO 的属性名,并初始化为 undefined。
  3. 第三步:将实参的值赋给对应的形参,使实参与形参保持一致。
  4. 第四步:在函数体内查找函数声明,并将函数体赋值给对应的属性。

为了更清晰地说明这一过程,我们可以结合一个具体案例进行分析,读者也可以先尝试推导结果,再对照后续步骤。


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

首先,创建 AO 对象,初始状态为空对象 {}。接着,查找形参和变量声明,将变量名和形参名作为 AO 的属性名,值设为 undefined。


{
 a: undefined,
 b: undefined,
}

随后,将传入的实参值赋给对应的形参,完成实参与形参的统一。


{
 a: 1,
 b: undefined,
}

最后,查找函数声明,并将函数体赋值给对应的属性。


{
 a: function a() {},
 b: undefined,
 d: function d() {}
}

因此,在函数 fn 执行的前一刻,变量 a、b、d 的值如上述 AO 对象所示。

基于此,调用 fn(1) 后的执行结果如下。

// ƒ a() {} // 123 // 123 // ƒ () {} // ƒ d() {}

在全局作用域中,预编译的过程与函数内部略有不同。

  • 首先创建全局对象(GO,Global Object),它等同于 window 对象,用于存储全局变量。
  • 接着查找形参(全局无)和变量声明,将变量名作为 GO 的属性名,值初始化为 undefined。
  • 最后查找函数声明,并将函数体赋值给对应的属性。

解释执行阶段

引擎开始逐行解释并执行代码。

实践练习题

以下提供几道练习题供读者参考和验证。


function test(a, b) {
 console.log(a);
 console.log(b);
 var b = 234;
 console.log(b);
 a = 123;
 console.log(a);
 function a() {}
 var a;
 b = 234;
 var b = function() {};
 console.log(a);
 console.log(b);
}
test(1);

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


function test() {
 console.log(b);
 if (a) {
  var b = 100;
 }
 c = 234;
 console.log(c);
}
var a;
test();
a = 10;
console.log(c);

总结

在大多数情况下,我们可以采用简化的方式来处理预编译过程。

  • 函数声明会整体提升。
  • 变量声明会进行声明提升。

如果遇到复杂情况,则必须采用最原始的预编译步骤来解决问题。

至此,关于 JavaScript 预编译机制的讲解到此结束。如需了解更多相关内容,可搜索本站往期文章或继续阅读后续文章,感谢大家的支持!

作者最新文章