【JS编译原理】V8执行JavaScript代码过程

news/2024/7/24 12:35:10 标签: javascript, babel

文章目录

  • 前言
  • 一、编译器和解释器
  • 二、V8执行JavaScript代码过程
    • 1.生成AST(抽象语法树)
    • 2.生成字节码
    • 3.生成机器码
  • 总结


前言

相信大家对Babel已经不陌生了,Babel充斥在我们代码中的每个角落。比如:jsx转化成js,es6转化成es5…

一切需要代码转化的事都可以使用babel来做,但我们有没有想过,babel是怎么去进行代码转化的呢,它转化的过程中发生了什么,让我们一起来了解一下js的编译原理吧!

一、编译器和解释器

在了解之前,我们先来理解一下编译器和解释器的概念吧。

我们知道,我们所编写的代码是不能直接被机器识别的,需要转化成能被机器识别的机器语言。我们都知道,javascript是一门解释型语言,后端之王java是一门编译型语言,我们根据语言的执行流程可以分为解释型编译型。我们来看看他们的区别吧。

  • 解释型语言:需要将代码转换成机器码,但是和编译型的区别在于运行时需要转换。比较显著的特点是,解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。
  • 编译型语言:在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果。

目前市面上有很多种 JS 引擎,其中比较现代的 JS 引擎就是 V8,它引入了 Java 虚拟机和 C++ 编译器的众多技术,和早期的 JS 引擎工作方式已经有了很大的不同。V8 是众多浏览器的 JS 引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。

编译器解释器翻译代码流程如下:

编译器:
在这里插入图片描述
解释器:

在这里插入图片描述
大概可以描述为:

编译器:

  1. 在代码的编译中,编译器首先会把代码经过词法分析和语法分析成AST
  2. 然后经过优化变成可执行的文件
  3. 如果过程中有出现语法等错误,将会报错且不会生成可执行文件。

解释器:

  1. 在代码的编译中,解释器首先会把代码经过词法分析和语法分析成AST
  2. 解释器还会将AST进行词义分析生成字节码
  3. 根据字节码来生成程序,执行结果。

回归正题,让我们来看一看V8是怎么执行JavaScript代码的!

二、V8执行JavaScript代码过程

通常来说,我们可以将V8执行过程分为4个阶段:

  • Parse 阶段:V8 引擎负责将 JS 代码转换成 AST(抽象语法树)
  • Ignition 阶段:解释器将 AST 转换为字节码,解析执行字节码也会为下一个阶段优化编译提供需要的信息
  • TurboFan 阶段:编译器利用上个阶段收集的信息,将字节码优化为可以执行的机器码
  • Orinoco 阶段:垃圾回收阶段,将程序中不再使用的内存空间进行回收

1.生成AST(抽象语法树)

此阶段分为两个步骤:

  1. 词法分析:讲代码语句分割成最小的单元,称为token。例如:let a = 0;,分割成let,a,=,0,;五个单元,js会自动忽略空格单元
  2. 语法分析:讲token数据通过语法检测转换成AST,若语法不规范,则抛出错误

tips:babel的原理就是利用AST,把代码进行转换。Eslint也是通过将代码转化成AST,然后通过语法分析检测代码规范性的问题。

2.生成字节码

为什么要生成字节码?如果讲AST直接转换成机器码的话,会出现下面的问题:

  • 直接转换会带来内存占用过大的问题,因为将抽象语法树全部生成了机器码,而机器码相比字节码占用的内存多了很多
  • 某些 JavaScript 使用场景使用解释器更为合适,解析成字节码,有些代码没必要生成机器码,进而尽可能减少了占用内存过大的问题

字节码就是介于 AST 和机器码之间的一种代码。需要将其转换成机器码后才能执行,字节码可以理解为是机器码的一种抽象。Ignition 解释器除了可以快速生成没有优化的字节码外,还可以执行部分字节码。

3.生成机器码

在 Ignition 解释器处理完之后,如果发现一段代码被重复执行多次的情况,生成的字节码以及分析数据会传给 TurboFan 编译器,它会根据分析数据的情况生成优化好的机器码。再执行这段代码之后,只需要直接执行编译后的机器码,这样性能就会更好。

对于 TurboFan 编译器,它是 JIT(即时编译) 优化的编译器,因为 V8 引擎是多线程的,TurboFan 的编译线程和生成字节码不会在同一个线程上,这样可以和 Ignition 解释器相互配合着使用,不受另一方的影响。

总结

本篇博客借鉴了多位大佬的文章,每次阅读都会有新的理解,只有了解底层原理才能让自己在前端的道路上越走越远。


http://www.niftyadmin.cn/n/861669.html

相关文章

【React Hooks优化】减少重复渲染

文章目录前言一、为什么要进行优化?React的默认渲染行为二、使用memo/useMemo缓存组件1.memo2.useMemo2.useCallback总结前言 事情发生在一个下午,我需要用React hooks写一个定时器,因为useEffect每次执行都会使组件重新渲染一次&#xff0c…

【前端怪谈】两个行内块元素之间的间距问题

文章目录前言一、问题描述二、解决方式1.使用浮动2.清除行内块元素之间的空格和换行符3.父元素设置font-size:03.父元素设置word-spacing总结前言 相信行内块元素,也就是display:inline-block的元素大家并不少见,不知道大家有没有发现一个问题,就是当两个行内块元素在同一行并…

【前端怪谈】js中为什么0.1+0.2 !== 0.3

文章目录前言一、问题原因二、解决办法三、大数相加1、BigInt2、大数相加总结前言 下面大家先来看一下这行诡异的代码,猜猜会输出什么 console.log(0.1 0.2);是不是和所想的不太一样?下面我们来探索一下为什么它会这样吧。 一、问题原因 在计算机中…

【前端优化】超详细!带你体验常用的前端优化手段

文章目录前言一、图片懒加载原因判断是否进入可视区方案一: clientHeight、scrollTop 和 offsetTop方案二:getBoundingClientRect二、防抖与节流三、路由和组件懒加载原因使用实例路由懒加载:组件懒加载四、图片预加载原因五、使用前端缓存强缓存协商缓存六、减少回…

【前端怪谈】为什么要用setTimeout模拟setInterval

文章目录前言一、setInterval存在的问题1.问题复现2.问题分析二、setInterval缺点及setTimeout1.setInterval缺点2.为什么setTimeout能取代setTimeout实现setInterval总结前言 大家都知道,setTimeout是延迟执行函数,而setInterval就像一个定时器&#x…

深入透析Promise实现细节(含手撕阶段)

文章目录前言一、Promise是什么?二、Promise核心逻辑实现1.基本原理2.新建类promise类,传入执行器executor3.传入resolve和reject方法4.then方法的简单实现5.完整代码及验证6.代码改进三.链式调用1.链式调用实现的基本思路2.then方法返回promise对象3.re…

闭包真的还会造成内存泄露吗?你不知道的闭包与垃圾回收!

文章目录前言一、闭包是什么?二、闭包有什么好处和坏处呢?1.好处2.坏处二、闭包会造成内存泄露吗?1.前言2.闭包会造成内存泄露吗,如果会为什么还会再react hooks中大量使用呢?三、为什么ie8及之前会造成内存泄露&#…

【JS垃圾回收】带你探索垃圾回收机制和Chrome V8垃圾回收机制

文章目录前言一、什么是垃圾回收?1.基本思路2.为什么要进行垃圾回收二、怎样进行垃圾回收1.标记清除优点缺点2.引用计数优点缺点三、Chrome V8垃圾回收机制1.为什么需要优化垃圾回收算法2.基本概念3.新生代垃圾回收器 - Scavenge4.老生代垃圾回收 - Mark-Sweep &…