重新了解JS引擎

2,348 阅读6分钟

前言

前段时间在看小黄书,里头讲了JS的编译原理,并提到了JS引擎,出于好奇,我想去了解相关方面的知识。查阅了大量资料后,我将知识点归纳吸收,便有了此文

为了方便阅读,我将文章分成一下几部分

image.png

什么是JS引擎

关于什么是JS引擎,我觉得结合生活中的例子会很好理解!

以生活中的汽车为例,汽车需要动,能让汽车动起来的那个东西就是引擎,如V8发动机。如果没有引擎,那仅仅是一堆废铜烂铁。

image.png

而JS引擎也是一样,就是让JS动起来的东西。

为了体现我的专业,引入百度百科对它的定义

JavaScript引擎是一个专门处理JavaScript脚本的虚拟机

可以看到,JS引擎本质是一个虚拟机或者说是一个程序,只是这个程序能处理我们的JS代码,让我们写JS代码动起来,从而发挥代码的威力。

那引擎具体做了哪些工作呢?

简单总结,JS引擎可以将 JS 代码编译为不同 CPU对应的汇编代码,而且还负责执行代码、分配内存以及垃圾回收等工作

image.png

常见的JS引擎有哪些

其实这也是我最开始学写篇文章的初衷,我在网上查阅了很多资料,但是发现文章都比较陈旧,而且各个引擎出现的时间杂乱无章,于是我希望自己可以按照引擎出现的时间线进行整理

下面按照各大引擎大致出现的顺序进行排列

  1. Mozilla的SpiderMonkey引擎,是第一款JavaScript引擎,早期用于 Netscape Navigator,现时用于 Mozilla Firefox。是用C语言实现的,还有一个Java版本叫Rhino;Rhino引擎由Mozilla基金会管理,开放源代码,完全以Java编写,用于 HTMLUnit;而后TraceMonkey引擎是基于实时编译的引擎,用于Mozilla Firefox 3.5~3.6版本;JaegerMonkey:结合追踪和组合码技术大幅提高性能,用于Mozilla Firefox 4.0以上版本

  2. Apple的JavaScriptCore ,简称JSC,开源,用于webkit内核浏览器,如 Safari ,2008 年实现了编译器和字节码解释器,升级为了 SquirrelFish。苹果内部代号为Nitro的 JavaScript 引擎也是基于 JSC引擎的。至于具体时间,JSC是WebKit默认内嵌的JS引擎,而WebKit诞生于1998年,Nitro是为Safari 4编写,Safari 4是2009年6月发布。

  3. Opera的LinearA:用于Opera4.0到6.1,Opera4于2000年6月发布;LinearB:用于Opera7.0到9.2,Opera7于2003年6月发布;Futhark:用于Opera9.5到10.2,Opera9.5于2008年6月发布;Carakan:用于Opera10.5.及以上 2009年12月

  4. Tamarin引擎,由Adobe Labs编写,Flash Player 9所使用的引擎,大概时间2006年6月

  5. 2008年9月,Google的V8引擎第一个版本随着Chrome的第一个版本发布。V8引擎用 C++编写,由 Google 丹麦开发,开源。除了Chrome,还被运用于Node.js以及运用于Android操作系统等

  6. Microsoft 的Chakra,译名查克拉,用于IE9、10、11和Microsoft Edge,IE9发布时间2011年3月

  7. JerryScript引擎 , 三星推出的适用于嵌入式设备的小型 JavaScript 引擎,2015年开源

  8. Nashorn引擎,从 JDK 1.8 开始,Nashorn取代Rhino(JDK 1.6, JDK1.7) 成为 Java 的嵌入式 JavaScript 引擎,JDK1.8发布于2014年

  9. QuickJS 引擎, 2019 年 7 月发布

  10. Hermes引擎,Facebook在Chain React 2019 大会上发布的一个崭新JavaScript引擎,用于移动端React Native应用的集成,开源

注:

  • 上述内容参考了大量资料所得,并不保证一定精准
  • 由于有些引擎找不到何时发布的具体时间,于是我把该引擎最早被应用的时间表示其发布时间

了解什么是JS引擎,也梳理常见的JS引擎,下面我们来看看引擎是如何运行JS代码的:以V8为例

V8引擎如何运行JS代码

image.png

首先我们简单来了解一下V8引擎

关于V8

V8引擎是使用C++编写的,由Google开源的JavaScriptWebAssembly引擎。V8第一个版本随着第一个版本的Chrome200892日发布。

V8因为它的高性能被很多人青睐,于是常见的如

  1. Chrome浏览器的JS引擎是V8
  2. Nodejs的运行时环境是V8
  3. electron的底层引擎也是V8

那V8具体都做了哪些工作呢?

V8主要职责

简单来说,V8是一个接收JavaScript代码,编译代码然后执行C++程序,编译后的代码可以在多种操作系统多种处理器上运行。其主要职责:

  1. 编译和执行JS代码
  2. 处理调用栈
  3. 内存分配
  4. 垃圾回收等

下面主要来看,V8如何编译和执行JS

V8如何编译和执行JS代码

一般来说,JS引擎在编译和执行代码都会用到三个重要的组件:

image.png

解析器parser

负责将JS源代码解析成抽象语法树AST,如下:

动画11111111.gif

JS代码变成AST的样子,你可以移步到ASTExplorer去看

解释器interpreter

解释器负责将AST解析成字节码bytecode,并可以将解析成的bytecode解释执行

444.gif

编译器compiler

编译器负责编译出运行更加高效的机器代码

5555.gif

但是在V8早期,在5.9版本以前,是没有解释器,但有两个编译器,其编译流程如下

  1. parser 解释器生成抽象语法树AST
  2. compiler 编译器Full-codegen 基准编译器 直接生成机器码
  3. 运行一段时间后,由分析器线程优化js代码
  4. compiler 编译器CrankShaft 优化编译器 重新生成AST提升运行效率

image.png

这样设计的缺点

  1. 机器码会占用大量的内存
  2. 缺少中间层机器码,无法实现一些优化策略
  3. 无法很好的支持和优化JS的新语特性,无法拥抱未来

正因为存在以上问题,新版本的V8流程上有所优化,流程如下

  1. parser 解析器 生成AST抽象语法树
  2. interpreter 解释器 Ignition 生成byteCode字节码 并直接执行
  3. 清除AST释放内存空间
  4. 得到25% - 50%的等效机器代码大小
  5. compiler 运行过程中,解释器收集优化信息发送给编译器TurboFan
  6. 重新生成机器码
  7. 有些热点函数变更会由优化后的机器码还原成字节码 也就是deoptimization 回退字节码操作执行

image.png

优化点:

  1. 值声明未调用,不会被解析生成AST
  2. 函数只被调用一次,bytcode直接被解释执行,不会进入到编译优化阶段
  3. 函数被调用多次,Igniton会收集函数类型信息,可能会被标记为热点函数,可能被编译成优化后的机器代码

好处:

  1. 由于一开始不需要直接编译成机器码,生成了中间层的字节码,从而节约了时间
  2. 优化编译阶段,不需要从源码重新解析,直接通过字节码进行优化,也可以deoptimization回退操作

以上就是本次关于JS引擎的所有内容!如果有误,欢迎留言告知,也欢迎点赞支持!

end