菜鸟入门第四天——JS预编译

前言

今天我将带领兄弟们学习预编译,这是许多大厂或面试时会经常出现的问题。希望通过我的讲解,能让兄弟们有所收获。

什么是预编译

预编译(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)并将其推入调用栈的底部。当程序调用一个函数,函数的执行上下文(包括变量环境、词法环境)被创建,并被推入调用栈的顶部。当函数执行完毕,它的执行上下文从调用栈的顶部弹出,控制权返回到调用该函数的上下文。如果函数内部调用其他函数,这些函数的执行上下文按照调用顺序被依次推入和弹出调用栈。

相关推荐
恋猫de小郭4 分钟前
Google I/O Extended :2025 Flutter 的现状与未来
android·前端·flutter
江城开朗的豌豆8 分钟前
Vue-router方法大全:让页面跳转随心所欲!
前端·javascript·vue.js
程序员爱钓鱼18 分钟前
Go语言泛型-泛型约束与实践
前端·后端·go
前端小巷子20 分钟前
web从输入网址到页面加载完成
前端·面试·浏览器
江城开朗的豌豆20 分钟前
Vue路由动态生成秘籍:让你的链接'活'起来!
前端·javascript·vue.js
晓得迷路了21 分钟前
栗子前端技术周刊第 88 期 - Apache ECharts 6.0 beta、Deno 2.4、Astro 5.11...
前端·javascript·echarts
江城开朗的豌豆26 分钟前
在写vue公用组件的时候,怎么提高可配置性
前端·javascript·vue.js
江城开朗的豌豆27 分钟前
Vue路由跳转的N种姿势,总有一种适合你!
前端·javascript·vue.js
江城开朗的豌豆27 分钟前
Vue路由玩法大揭秘:三种路由模式你Pick谁?
前端·javascript·vue.js
江城开朗的豌豆28 分钟前
Vue路由守卫全攻略:给页面访问装上'安检门'
前端·javascript·vue.js