前言
今天我将带领兄弟们学习预编译,这是许多大厂或面试时会经常出现的问题。希望通过我的讲解,能让兄弟们有所收获。
什么是预编译
预编译(Precompile)是指在程序执行之前,将程序源代码或某些代码片段提前进行编译,以便在运行时能够更快地执行其主要目的是提高程序的执行效率和性能。
预编译分为两种,一种叫做函数预编译
,另一种叫做全局预编译
,函数预编译发生在函数执行的前,全局预编译发生在页面加载完成时。
函数预编译
话不多说,下面我将通过这个案例来帮兄弟们分析函数预编译过程。
js
function fn(a){
console.log(a); // function a() {}
var a = 123
console.log(a); // 123
function a() {} // 函数声明
console.log(a); // 123
var b = function() {} // 函数表达式
console.log(b); // function () {}
function d(){}
var d = a
console.log(d); // 123
}
AO:{
// a: undefined 1 function a() {} 123,
// b: undefined function () {},
// d: undefined function d() {} 123,
}
fn(1)
预编译发生在函数执行之前 (四部曲)
1.创建AO对象 (Action Object)
在函数执行前,JavaScript引擎会创建一个特殊的对象,叫做Action Object(AO)。这个对象用于存储函数内的局部变量、形参以及函数声明。
js
AO:{
}
2.找形参和变量声明,将变量声明和形参作为AO的属性名,值为undefind
JavaScript引擎会扫描函数内部,找到所有的形参和变量声明。然后,它将这些变量声明和形参作为AO对象的属性名,初始值为undefined。
js
AO:{
a: undefined
b: undefined
d: undefined
}
3.将实参和形参值统一
js
AO:{
a: undefined -> 1
b: undefined
d: undefined
}
4.在函数体内找函数声明,将函数名作为AO对象的属性名,值赋予函数体
js
AO:{
a:undefined -> 1 -> function a(){} -> 123
b:undefined -> function b(){}
d:undefined -> function d(){} -> 123
}
代码已经分析完了,下面给大家来看看运行结果:

全局预编译
下面我将通过另外一个案例来帮兄弟们分析全局预编译过程。
js
GO:{
global: undefined 100,
fn: function fn() {}
}
var global = 100
function fn() {
console.log(global);
}
AO:{
//由于函数内部没有形参和声明变量,也没有函数声明,所以AO对象内为空
}
fn()
预编译发生在全局(三部曲)
1.创建 GO 对象(Global Object)
全局预编译阶段开始时,JavaScript引擎会创建一个全局对象,通常称为Global Object(GO)。这个对象是全局作用域中的根对象。
js
GO:{
}
2.找变量声明,将变量声明作为GO的属性名,值为undefined
引擎会扫描全局作用域,找到所有的变量声明,并将它们作为GO对象的属性名,初始值设置为undefined。
js
GO:{
global: undefined,
fn: function fn() {}
}
3.在全局找函数声明,将函数名作为GO对象的属性名,值赋予函数体
在全局作用域内,引擎还会找到函数声明,将函数名作为GO对象的属性名,同时将整个函数体赋给这个属性。
js
GO:{
global: undefined -> 100
fn: function fn(){}
}
代码已经分析完了,下面给大家来看看运行结果:

结语
让我们通过这张图来分析一下JS预编译的过程

当JavaScript程序开始执行,会创建一个全局执行上下文(GO)并将其推入调用栈的底部。当程序调用一个函数,函数的执行上下文(包括变量环境、词法环境)被创建,并被推入调用栈的顶部。当函数执行完毕,它的执行上下文从调用栈的顶部弹出,控制权返回到调用该函数的上下文。如果函数内部调用其他函数,这些函数的执行上下文按照调用顺序被依次推入和弹出调用栈。