JS执行中的编译和解释

编译器和解释器

本文涉及到的概念有 编译器(Compiler)、解释器(Interpreter)、抽象语法树(AST)、字节码(Bytecode)、即时编译器(JIT)

JavaScript是一个编译和解释都存在的一个语言。

V8是如何执行一段JavaScript代码的

image.png
V8的执行过程中是既有解释器 Ignition ,又有编译器 TurboFan。分析一下上面的流程:

  1. 生成抽象语法树AST和执行上下文

    AST是如何生成的,分为两个阶段。

    1. 第一个阶段是::分词(tokenize)::,也叫词法分析,实际就是将源码拆解成一个个token。

image.png
上面的 varmyName=”极客时间”

  1. 第二个阶段是::解析(parse)::,又称为语法分析,作用是将上一步生成的token数据,根据语法规则转换成AST。源码符合语法规则,就转换成功,若源码有语法作物,就会终止,并抛出一个语法错误。

::此时将源代码转换成抽象语法树,并生成对应的这段代码的执行上下文::。抽象语法树AST类似是渲染引擎处理完HTML后生成计算机可理解的DOM树

结合代码

var myName = "极客时间"
function foo(){
  return 23;
}
myName = "geektime"
foo()
复制代码

以上这段代码生成AST如下

Image

image.png
AST 的结构和代码的结构非常相似,其实你也可以把 AST 看成代码的结构化的表示,编译器或者解释器后续的工作都需要依赖于 AST,而不是源代码

AST是一种非常重要的数据结构,在很多项目中都有使用,::比如Babel,一个用于将ES6转换为ES5的代码转换器,原理就是讲ES6源码先转换成AST,然后再将ES6代码转换成的AST转换成ES5语法的AST,最后根据ES5的AST生成字节码,又比如ESLint,一个用于检查JavaScript代码规范的插件,检查流程也是先将源码转换成AST,然后利用AST来检查代码的规范化。::

  1. 生成字节码

    此时已经有了AST和执行上下文,然后就是::解释器Ignition,用来转换AST生成字节码,并且解释执行字节码::。

    为什么要有字节码?

    一开始V8并没有字节码,全部转换为机器码,虽然这样代码的执行效率很高,但是由于全转为机器码太占内存,所以就引入了字节码。

Image.png

可以看到同样的代码字节码比机器码要更节约空间,但是字节码可以减少系统的内存使用。

  1. 执行代码

    生成字节码后,就进入执行阶段了。

    对于第一执行的字节码,解释器Ignition会逐条解释执行,::解释器Ignition除了负责生成字节码之外,还可以解释执行字节码。::在解释器执行过程中如果发现有热点代码,::比如一段代码重复执行多次,后台的编译器TurboFan就会将这部分字节码转换为机器码并且保存下来::,然后再执行这部分热点代码的时候,只需要执行编译器转换后的机器码就可以了,会大大提升代码的执行效率。

字节码结合解释器和编译器这种技术叫做即时编译(JIT),在Java和Python中的虚拟机也是基于这种技术实现的。在V8中,就是解释器在执行字节码的同时,检测代码情况,当发现有一部分代码变热之后,编译器就会将热点代码对应的字节码转换为机器码保存起来,下次再执行就直接执行机器码。

JIT工作机制

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享