本文详细解析 JavaScript 代码执行的三个步骤:语法分析、预编译与解释执行。重点阐述函数执行前的预编译过程,包括创建 AO 对象、处理形参与变量声明、实参赋值及函数声明提升。同时对比全局作用域下的 GO 对象创建规则,并通过具体代码示例展示变量与函数在预编译阶段的行为差异,帮助开发者深入理解变量提升与作用域机制。
JavaScript 引擎在执行脚本时,遵循特定的工作流程。
整个执行过程可以划分为三个主要阶段。
- 第一阶段是语法分析。
- 第二阶段是预编译。
- 第三阶段是解释执行。
JavaScript 引擎在处理代码时,首先会进行全面的语法检查,确保没有低级错误;随后进入预编译阶段,构建内部逻辑结构;最后才开始逐行解释并执行代码。
语法分析阶段
在代码实际运行之前,系统会扫描整个代码库,检测是否存在诸如括号缺失等基础语法错误。
预编译阶段
预编译操作发生在函数即将执行的前一刻。
如果变量在未经声明的情况下直接赋值,该变量会被挂载到全局对象上。
a = 3
var b = c = 4
所有在全局作用域中声明的变量,都会成为全局对象(如浏览器中的 window)的属性。
var a = 1 ===> window.a = 1
预编译的具体步骤可分为四步。
- 第一步:创建执行期上下文对象(AO,Activation Object)。该对象用于存储函数内部的局部变量。
- 第二步:查找形参和变量声明。将形参名和变量名作为 AO 的属性名,并初始化为 undefined。
- 第三步:将实参的值赋给对应的形参,使实参与形参保持一致。
- 第四步:在函数体内查找函数声明,并将函数体赋值给对应的属性。
为了更清晰地说明这一过程,我们可以结合一个具体案例进行分析,读者也可以先尝试推导结果,再对照后续步骤。
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 预编译机制的讲解到此结束。如需了解更多相关内容,可搜索本站往期文章或继续阅读后续文章,感谢大家的支持!