JS引擎的执行过程

基础概念

全面分析js引擎的执行过程,分为三个阶段:
1.语法分析
2.预编译阶段
3.执行阶段

说明:
1.浏览器先按照js的顺序加载script标签分隔的代码块,js代码块加载完毕之后,
2.立刻进入到上面的三个阶段
3.然后再按照顺序找下一个代码块
4.再继续执行三个阶段
5.无论是外部脚本文件(不异步加载)还是内部脚本代码块,都是一样的,并且都在同一个全局作用域中。

语法分析

js的代码块加载之后,会首先进入到语法分析阶段,该阶段的主要作用:
分析该js脚本代码块的语法是否正确,如果出现不正确会向外抛出一个语法错误,停止改js代码的执行,然后继续查找并加载下一个代码块;如果语法正确,则进入到预编译阶段。

预编译阶段

js代码块通过语法分析阶段之后,语法都正确的会进入预编译阶段。
在分析预编译阶段之前,我们先了解一下js的运行环境,运行环境主要有三种:
1.全局环境(js代码加载完毕之后,进入到预编译也就是进入到全局环境)
2.函数环境(函数调用的时候,进入到该函数环境,不同的函数,函数环境不同)
3.eval环境(不建议使用,存在安全、性能问题)
每进入到一个不同的运行环境都会创建一个响应的执行上下文,那么在一段js程序中一般都会创建多个执行上下文,js引擎会以栈的数据结构对这些执行进行处理,形成函数调用栈,栈底永远是全局执行上下文,栈顶则永远是当前的执行上下文。
函数调用栈
什么是函数调用栈?
函数调用栈就是使用栈存取的方式来进行管理运行环境,特点是先进后出,后进先出。
创建执行上下文
执行上下文可以理解成当前的执行环境,与该运行环境相对应。创建执行上下文的过程中,主要做了下面三件事

  • 创建变量对象
  • 创建作用域链
  • 确定this的指向

执行阶段

首先来思考一个问题:
js是单线程的,那么是否代表参与js执行过程的线程就只有一个?
答:不是的,会有四个线程参与该过程,但是永远只有JS引擎线程在执行JS脚本程序,其他的三个线程只协助,不参与代码解析与执行。参与js执行过程的线程分别是:
(1)JS引擎线程:也称为JS内核,负责解析执行javascript脚本程序的主线程(例如V8引擎)
(2)事件触发线程:归属于浏览器内核进程,不受JS引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发的时候,事件触发线程就会把该事件的处理函数推进事件队列,等待JS引擎线程执行。
(3)定时器触发线程:主要控制计时器setInterval和延时器setTimeout,用于定时器的计时,计时完毕,满足定时器的触发条件,则将定时器的处理函数推进事件队列中,等待JS引擎线程执行。
注:W3C在HTML标准中规定setTimeout低于4ms的时间间隔算为4ms
(4)HTTP异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎线程执行。

JS引擎的工作原理

JS引擎的工作原理基本都大同小异,整体流程分为以下步骤:
1.将JS代码解析为AST(抽象语法树)
2.基于AST,解释器(interpreter)将AST转化为字节码(bytecode),这一步js引擎实际上已经在执行代码了。
3.为了进一步的优化,优化编译器(optimizing compiler)将热点函数优化编译为机器指令()
4.如果优化假设失败,优化编译器会将机器码回退到字节码。

各大主流浏览器js引擎对比

在了解V8的具体工作流程之前,我们先看一下各大浏览器的js引擎具体的工作流程是怎样的。
上面也提到,js引擎工作的流程大致是一致的:

  • 解释器负责快速的生成没有优化过的字节码
  • 优化编译器负责生成优化过后的机器码但是相对来说花的时间会长一些。
V8引擎

V8引擎用于chorme和NodeJs中,V8的解释器叫做Ignition,负责产生和执行字节码。
Ignition在执行字节码的过程中会收集分析数据用于后续的优化。例如当一个函数经常被调用执行,这个函数就会变成热点函数,这个时候字节码和分析数据就被传递给优化编译器作进一步的优化的处理。
V8的优化编译器叫做TurboFan,根据分析数据产生高度优化的机器指令。

SpiderMonkey引擎

SpiderMonkey是Mozilla的js引擎,用于FirefoxSpiderNode
Spider有两个优化编译器。解释器将字节码交给Baseline编译器,Baseline编译器会优化代码并执行,执行过程中会产生分析数据。结合分析数据IonMonkey编译器会生成高度优化的代码。如果优化失败,IconMonkey会将代码回滚到Baseline产生的代码。

Chakra引擎

Chakra是微软的js引擎,用于Edge和Node-ChakraCore。
Chakra同样也有两个优化编译器。解释器将字节码交给SimpleJIT编译器,SimpleJIT做部分优化。结合分析数据FullJIT编译器会生成高度优化的代码。如果优化失败,FullJIT将代码回滚到字节码。

Javascript引擎

JavascriptCore,是苹果的js引擎用于Safari和React Native。
JSC引入了三个优化编译器。LLInt解释器(the Low-Level Interpreter)将代码交给Baseline编译器,Baseline优化后将代码交给DFG(Data Flow Graph)编译器,最后DFG将代码交给FTL(Faster Than Light)编译器。

通过对比其实不难发现,js引擎的整体框架基本都是相同的,都是parser->interpreter->compiler。那为什么有些只有一个优化编译器而有些又有多个呢?其实是为了做一些权衡。

解释器能够快速的生成可执行的代码,但是代码的执行效率不高。编译器需要多花时间来做编译优化,但是最后生成的是可以高效执行的机器码。所以这里就需要权衡到底是要快速的生成并执行还是要多花些时间生成并高效执行,一些引擎引入多个具有不不同时间/效率特性的优化编译器,以增加复杂性为代价,就是为了对这些权衡做更细粒度的控制。这中间还涉及到内存的权衡,机器码比字节码会占用更多的内存

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值