浏览器原理 13 # 编译器和解释器:V8是如何执行一段JavaScript代码的?

简介: 浏览器原理 13 # 编译器和解释器:V8是如何执行一段JavaScript代码的?

必知概念

深入理解 V8 的工作原理,需要搞清楚的一些概念,下面的解释部分来自百科。




编译器(Compiler)


   1、简单讲,编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。


   2、一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)


   3、高级计算机语言便于人编写,阅读交流,维护。机器语言是计算机能直接解读、运行的。


   4、编译器将汇编或高级计算机语言源程序(Source program)作为输入,翻译成目标语言(Target language)机器代码的等价程序。


   5、源代码一般为高级语言 (High-level language), 如Pascal、C、C++、Java、汉语编程等或汇编语言,而目标则是机器语言的目标代码(Object code),有时也称作机器代码(Machine code)。


   6、对于C#、VB等高级语言而言,此时编译器完成的功能是把源码(SourceCode)编译成通用中间语言(MSIL/CIL)的字节码(ByteCode)。最后运行的时候通过通用语言运行库的转换,编程最终可以被CPU直接计算的机器码(NativeCode)。



解释器(Interpreter)

   解释器(英语:Interpreter),又译为直译器,是一种电脑程序,能够把高级编程语言一行一行直接转译运行。解释器不会一次把整个程序转译出来,只像一位“中间人”,每次运行程序时都要先转成另一种语言再作运行,因此解释器的程序运行速度比较缓慢。它每转译一行程序叙述就立刻运行,然后再转译下一行,再运行,如此不停地进行下去。


抽象语法树(AST)

在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。


字节码(Bytecode)

   字节码(Byte-code)是一种包含执行程序、由一序列 op 代码/数据对组成的二进制文件。字节码是一种中间码,它比机器码更抽象。它经常被看作是包含一个执行程序的二进制文件,更像一个对象模型。字节码被这样叫是因为通常每个 opcode 是一字节长,但是指令码的长度是变化的。每个指令有从 0 到 255(或十六进制的: 00 到FF)的一字节操作码,被参数例如寄存器或内存地址跟随。



即时编译器(JIT)

   即时编译器(just-in-time compiler)随虚拟机一起供给的,并可选使用。它把字节码编译成可立即执行的指定平台的可执行代码。其实字节码配合解释器和编译器是最近一段时间很火的技术,比如 Java 和 Python 的虚拟机也都是基于这种技术实现的,我们把这种技术称为即时编译(JIT)。除了 V8 使用了“字节码 +JIT”技术之外,苹果的 SquirrelFish Extreme 和 Mozilla 的 SpiderMonkey 也都使用了该技术。


JIT 的工作过程:


20210410122401280.png


热点代码(HotSpot)

比如一段代码被重复执行多次,这种就称为热点代码。




编译器和解释器

按语言的执行流程,可以把语言划分为:


   编译型语言:需要经过编译器的编译过程,并且编译之后会直接保留机器能读懂的二进制文件,这样每次运行程序时,都可以直接运行该二进制文件,而不需要再次重新编译。比如 C/C++、GO 等都是编译型语言。


   解释型语言:每次运行时都需要通过解释器对程序进行动态解释和执行。比如 Python、JavaScript 等都属于解释型语言。


编译型语言执行流程:


20210410115645633.png

解释型语言执行流程:

20210410115703280.png



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


V8 执行一段代码流程图:解释器 Ignition (点火器),编译器 TurboFan (涡轮增压)

20210410115741552.png


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


AST 是如何生成的?

第一阶段是分词(tokenize),又称为词法分析


其作用是将一行行的源码拆解成一个个 token。所谓 token,指的是语法上不可能再分的、最小的单个字符或字符串。你可以参考下图来更好地理解什么 token。


20210410120917491.png



第二阶段是解析(parse),又称为语法分析

其作用是将上一步生成的 token 数据,根据语法规则转为 AST。如果源码符合语法规则,这一步就会顺利完成。但如果源码存在语法错误,这一步就会终止,并抛出一个“语法错误”。


2. 生成字节码


有了 AST 和执行上下文后,解释器 Ignition 会根据 AST 生成字节码,并解释执行字节码。

注意


  • 开始 V8 并没有字节码,而是直接将 AST 转换为机器码
  • Chrome 运行在的手机上(特别是 512M 内存),内存占用问题:消耗大量的内存来存放转换后的机器码
  • 为了解决内存占用问题,引入字节码


字节码和机器码占用空间对比如下图:机器码所占用的空间远远超过了字节码

20210410121619382.png


字节码就是介于 AST 和机器码之间的一种代码。但是与特定类型的机器码无关,字节码需要通过解释器将其转换为机器码后才能执行。



3. 执行代码


   在 Ignition 执行字节码的过程中,如果发现有热点代码(HotSpot),那么后台的编译器 TurboFan 就会把该段热点的字节码编译为高效的机器码,然后当再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了,这样就大大提升了代码的执行效率。



拓展


JavaScript 的性能优化


  1. 提升单次脚本的执行速度,避免 JavaScript 的长任务霸占主线程,这样可以使得页面快速响应交互;
  2. 避免大的内联脚本,因为在解析 HTML 的过程中,解析和编译也会占用主线程;
  3. 减少 JavaScript 文件的容量,因为更小的文件会提升下载速度,并且占用更低的内存。





目录
相关文章
|
3天前
|
JavaScript 前端开发 测试技术
编写JavaScript模块化代码主要涉及将代码分割成不同的文件或模块,每个模块负责处理特定的功能或任务
【5月更文挑战第10天】编写JavaScript模块化代码最佳实践:使用ES6模块或CommonJS(Node.js),组织逻辑相关模块,避免全局变量,封装细节。利用命名空间和目录结构,借助Webpack处理浏览器环境的模块。编写文档和注释,编写单元测试以确保代码质量。通过这些方法提升代码的可读性和可维护性。
8 3
|
12天前
|
JavaScript 前端开发 开发工具
【JavaScript 与 TypeScript 技术专栏】TypeScript 如何提升 JavaScript 代码的可读性与可维护性
【4月更文挑战第30天】TypeScript 提升 JavaScript 代码的可读性和可维护性,主要通过静态类型系统、增强代码组织、智能提示与错误检测、文档化和在大型项目中的优势。静态类型减少误解,类和接口提供结构,智能提示提高编码效率,类型注解充当内置文档。在大型项目中,TypeScript 降低理解差异,平滑迁移现有 JavaScript 项目,助力提高开发效率和项目质量。
|
13天前
|
前端开发 JavaScript
浏览器通过构建DOM树来解析HTML代码
【4月更文挑战第30天】浏览器通过构建DOM树来解析HTML代码
24 1
|
19天前
|
JavaScript 前端开发 算法
< JavaScript小技巧:如何优雅的用【一行代码 】实现Js中的常用功能 >
在开发中,采用简洁的语法和结构,遵循一致的命名规范,具有良好的代码组织和注释,能很好的提高代码的质量。可读性:易于阅读和理解。清晰的命名、简洁的语法和良好的代码结构可以使代码的意图更加明确,降低理解代码的难度,提高代码的可读性。可维护性:易于维护。当代码逻辑清晰、结构简洁时,开发者可以更快速地定位和修复bug,进行功能扩展或修改。同时,可读性高的代码也有助于后续的代码重构和优化。可扩展性:更具有扩展性和灵活性。清晰的代码结构和简洁的代码风格使得添加新功能、修改现有功能或扩展代码更加容易。
< JavaScript小技巧:如何优雅的用【一行代码 】实现Js中的常用功能 >
|
20天前
|
前端开发 JavaScript 容器
JavaScript、CSS像素动画特效代码
此示例创建一个带有像素粒子的容器,每隔300毫秒就会动态添加一个新的像素粒子,然后通过CSS的关键帧动画(`@keyframes`)使它们产生上升和逐渐消失的动画效果。你可以根据需要修改像素粒子的颜色、大小、动画效果和创建速度。
14 0
|
26天前
|
JavaScript
js校验统一社会信用代码
js校验统一社会信用代码
27 0
|
21天前
|
JavaScript
浏览器插件crx文件--JS混淆与解密
浏览器插件crx文件--JS混淆与解密
21 0
|
1月前
|
JavaScript 前端开发 UED
JS:如何获取浏览器窗口尺寸?
JS:如何获取浏览器窗口尺寸?
45 1
|
4月前
|
JavaScript 前端开发 算法
Node.js中的process.nextTick与浏览器环境中的nextTick有何不同?
Node.js中的process.nextTick与浏览器环境中的nextTick有何不同?
|
4月前
|
JavaScript 前端开发 小程序
js 实现浏览器下载视频2种方法
js 实现浏览器下载视频2种方法
485 0