第一章 JavaScript 引擎的内部世界 - 从 V8 到 D8 的学习之旅

284 阅读9分钟

什么是V8

V8 是由 Google 开发的开源 JavaScript 引擎,目前用在Chrome浏览器和Node.js中,也被称为虚拟机,其核心功能是执行易于人类理解的JavaScript代码,模拟实际计算机各种功能 CPU、堆栈、寄存器等 来实现代码的编译和执行,所以对于JavaScript代码来说,V8就是它的整个世界。


那么V8又是怎么执行JavaScript代码的呢

其主要核心流程分为编译和执行两步。首先需要将JavaScript代码转换为低级中间代码或者机器能够理解的机器代码,然后再执行转换后的代码并输出执行结果。


计算机为什么要编译 JavaScript 后在进行执行呢?

从前,计算机的世界是由一串串二进制的机器指令组成的。这些二进制指令是计算机CPU能直接识别和执行的语言,就好像人类在与机器沟通时必须使用他们能理解的语言。但是对于人类来说,记住和编写这些晦涩难懂的二进制指令实在是太过繁琐和困难了。

于是聪明的工程师们想出了一个办法 - 引入了汇编语言。汇编语言比二进制指令更加人性化,使用了一些助记符号来代表不同的操作,大大降低了程序员的编程难度。但是计算机的CPU依然无法直接识别和执行汇编语言,它们只能理解最底层的二进制机器指令。因此,在使用汇编语言编写程序之后,还需要一个特殊的工具 - 汇编器,来将汇编代码翻译成CPU可以执行的二进制机器码。

随着计算机技术的不断进步,人们又发明了更高级的编程语言,如C语言、Java语言、Python等。这些语言更加接近人类的思维方式,语法更加优雅简单,让程序员可以更专注于业务逻辑的实现,而无需过多关注底层硬件架构的细节。

但是,这些高级语言同样无法直接被CPU执行。于是,编程语言的编译器解释器应运而生,它们的作用就是将高级语言代码转换成计算机能理解的二进制机器码。编译器会先对代码进行语法分析、中间代码生成、目标代码生成等一系列复杂的处理,最终生成优化后的机器码,让CPU能够高效地执行程序。


解释器 - 解释执行

解释器就像是一个翻译官,它的工作是将高级语言代码翻译成 CPU 能够理解的机器码。但和编译器不同的是,解释器是边翻译边执行的。它会一行一行地读取高级语言代码,立即将其翻译成机器码并执行,而不需要先把整个程序全部编译成机器码再执行。如下图:

这样做的好处是,程序可以立即开始运行,而不需要经历复杂的编译过程。但缺点是,由于每次都需要边翻译边执行,效率相对编译器要低一些。

解释执行,需要先将输入的源代码通过解析器编译成中间代码,之后直接使用解释器解释执行中间代码,然后直接输出结果。


执行器 - 编译执行

而执行器就是负责真正运行这些被翻译好的机器码的部件。它就像一个勤劳的工人,按照解释器提供的指令一步一步地执行程序,完成各种计算和操作。如下图:

执行器会严格按照机器码的语义去执行每一条指令,将程序的功能一点一点地实现出来。它不需要关心这些指令是如何从高级语言翻译而来的,只需专注于高效地执行它们即可。

编译执行,需要先将源代码转换为中间代码,然后我们的编译器再将中间代码编译成机器代码。通常编译成的机器代码是以二进制文件形式存储的,需要执行这段程序的时候直接执行二进制文件就可以了


以上就是执行高级语言的基本方式,但是不同的高级语言,如 C、C++, 只需要将其编译为二进制的文件,然后直接执行二进制代码,但是java、JavaScript等语言,则需要不同的虚拟机模拟计算机的编译执行。

前言说到,V8 是虚拟机

在 V8 出现之前,所有的 JavaScript 虚拟机所采用的都是解释执行的方式,这是 JavaScript 执行速度过慢的一个主要原因。

V8出现后,它采用混合编译执行解释执行这两种手段,给 JavaScript 的执行速度带来了极大的提升,来对JavaScript代码进行编译与解释,通常把这种混合使用编译器和解释器的技术称为JIT(Just In Time) 即时编译 技术。

为什么要采用混合编译执行和解释执行

因为这两种方法都各自有各自的优缺点,解释执行的启动速度快,但是执行时的速度慢,而编译执行的启动速度慢,但是执行时的速度快

V8 执行一段 JavaScript 的流程图:

V8 执行 JavaScript 代码的流程

初始化基础环境 - 解析源码生成 AST 和作用域 - 依据 AST 和作用域生成字节码 - 解释执行字节码 - 监听热点代码 - 优化热点代码为二进制的机器代码 - 反优化生成的二进制机器代码


了解了V8后,什么是D8呢?

为了更好地开发和调试 V8 引擎本身,Google 团队创建了 d8 (d8 is debug for V8) 这个独立的命令行工具,它的主要目的就是为 V8 开发者提供各种调试和分析功能

安装V8 debug 需要版本 node 14+
先安装 jsuv

jsvu 是 JavaScript(引擎)版本更新器

Github 链接 Jsuv

npm install -g jsvu


推荐使用该命令安装
jsvu --os=win64 --engines=v8-debug 

// 如果是mac 或者 linux 版本的替换版本就行, 插件只需要下载一个v8-debug


安装 eshost-cli

eshost-cli 使在 jsvu 安装的所有 JavaScript 引擎中运行和比较代码变得容易

npm install -g eshost-cli

由于我是window电脑 我这里安装以下命令


eshost --add "V8 --harmony" d8 "%USERPROFILE%.jsvu\bin\v8.cmd" --args "--harmony"

二者安装其一,推荐安装上面的

eshost --add "V8" d8 "%USERPROFILE%.jsvu\bin\v8.cmd"

解释一下两个命令的区别,

V8 --harmony 环境,在这个环境下,--harmony 标志可以启用一些 ECMAScript 的新特性,这些特性可能还处于试验或预发布阶段,使用这个环境可以测试和使用 JavaScript 语言的最新实验性特性

V8 环境,在这个环境下 不会使用任何额外的参数,它会执行标准的 ECMAScript 语言特性,不会启用任何实验性的新功能,这个环境更适合用于生产环境或需要稳定性的场景

安装完后,查看列表

eshost --list


启动方法 V8 --harmony 环境

eshost -h "V8 --harmony" -e "console.log('Using --harmony');"

V8 环境

eshost -h "V8" -e "console.log('Regular V8');"
注意,如果安装完后,eshost iist 列表有 但是启动出错 请排查以下问题
启动的时候报错, eshost 命令无法找到 v8.cmd 文件而引起的,按照如下图排查解决。
首先先确定路径有没有问题,也就是 %USERPROFILE% 环境变量 这个不用手动改,powershell会自动识别
v8.cmd 文件实际上不存在于 .jsvu\bin 目录中,就像下图,实际文件是v8-debug.cmd

不用环境变量执行,直接使用绝对路径执行 C:\Users\cheng 这一段替换成你自己电脑的路径 & "C:\Users\cheng\.jsvu\bin\v8-debug.cmd" --harmony

看到这个就是启动成功了


启动 d8

注意查看备注内容!!!

// 需要在运行  Windows PowerShell 同一界面创建文件,就像启动服务要cd到同一文件下一样
d8 --print-ast test.js


test.js 内容

```
var test = "D8 install successfully"

```

执行这段命令之后,D8会打印出如下内容

--- AST ---
FUNC at 0
. KIND 0
. LITERAL ID 0
. SUSPEND COUNT 0
. NAME ""
. INFERRED NAME ""
. DECLS
. . VARIABLE (0x7ff0e3022298) (mode = VAR, assigned = true) "test"
. BLOCK NOCOMPLETIONS at -1
. . EXPRESSION STATEMENT at 11
. . . INIT at 11
. . . . VAR PROXY unallocated (0x7ff0e3022298) (mode = VAR, assigned = true) "test"
. . . . LITERAL "D8 install successfully"

上面这个结构就是AST,它就是JS源代码的结构化表述,AST是个树状结构,直观地理解,你可以将其转换为一个图形树,如下图所示:


总结

V8是由Google开发的开源JavaScript引擎,被广泛应用于Chrome浏览器和Node.js中。它是JavaScript代码的执行引擎,模拟了计算机CPU、堆栈等功能来实现JavaScript代码的编译和执行,V8采用了混合编译执行和解释执行的方式,这是为了利用两种方法的优点,解释执行启动快但执行慢,编译执行启动慢但执行快。 V8的执行流程包括:

  • 初始化基础环境
  • 解析源码生成抽象语法树(AST)和作用域
  • 根据AST和作用域生成字节码
  • 解释执行字节码
  • 监听热点代码
  • 优化热点代码为二进制机器代码
  • 在需要时反优化生成的二进制机器代码

这种混合执行方式,即"即时编译(JIT)"技术,大大提升了JavaScript的执行速度。

D8 是一个用于开发和调试 V8 引擎的强大工具,为 V8 开发者提供了丰富的分析和调试功能。