传奇广告查询第一站 同步54.com

V8引擎:编译器和解析器是如何执行一段javascript代码的?
原创 于2026-01-05 18:07:00发布
9 阅读
0
0

前言

前面我已经说完了js引擎是如何存储数据,如何回收垃圾的,但这只是基础罢了,并不是v8引擎的最主要任务,接下来我要讲v8引擎的核心:如何执行js代码,为什么要学习这些东西呢?
前端工具和框架的自身更新速度非常快,github上开源的项目和工具也是层出不穷,要想追赶上这些工具的更新速度,快速学习,我们必须理解其底层原理,抓住那些本质的知识,万变不离其宗,无论前端框架工具怎么变,其实都是建立在原来的原生js之上的,我们理解了这些底层知识,相应的自然能快速理解这些上层应用了,也能帮助大家深入理解babel,语法检查工具eslint,前端矿界Vue和React的一些底层实现机制

接下来的篇幅可能涉及刀一些概念和原理,比如编译器,解释器,抽象语法树,字节码,即时编译器(JIT)等概念,如果读者不了解这些,可以自行查找资料大概了解一下

编译器和解释器

之所以存在编译器和解释器是因为机器是不能直接理解我们所写的代码,所以在执行程序之前,需要将我们的代码翻译成机器能听懂的机器语言,按照语言的执行流程,可以把语言分为编译型语言和解释型语言.

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

而由解释型语言编写的程序,每次运行 时都需要通过解释器堆程序进行动态解释和执行.比如python,JavaScript都属于解释下语言

那编译器和解释器如何翻译代码的呢?大致是可以分为如下:

  • 在编译型语言的编译过程中编译器首先会依次对源代码进行词法分析,语法分析,生成抽象语法树,然后是优化代码,最后生成处理器能够理解的机器码.如果编译成功,将会生成一个可执行文件.但如果编译失败了发生了语法错误等其他错误,编译器就会抛出异常,最后的二进制文件也不会生成成功
  • 解释下语言解释的过程中,同样解释器也会对源代码进行词法分析,语法分析,并生成抽象语法树,不过他会基于抽象语法树生成字节码,最后在根据字节码来执行程序,输出结果

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

通过上面的介绍,我们才能一览全局,接下来我们来说说v8的工作流程

源代码->词法分析.语法分析->AST&执行上下文->通过解释器生成字节码逐行运行->运行量多的代码编译执行生成机器码 

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

将源代码转换为抽象语法树,并且生成执行上下文,而执行上下文我在前面的文章中已经介绍过很多次了,主要是代码在执行过程中的环境信息

那么我们下面就得重点讲解下抽象语法树了(AST),看看什么是AST和生成AST的过程是怎样的.

高级语言是大多数开发者可以理解的语言,但是让编译器或者解释器来理解就非常困难了,对于编译器或者解释器来说,他们可以理解的就是AST了,所以无论你使用的是解释下语言还是编译型语言,在编译过程中,他们都会生成一个AST,这和渲染引擎将html格式文件转换为计算机可以理解的dom树的情况类似.(学过编译原理的同学应该可以很好的理解)

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

AST是非常著名的一种数据结构,在很多项目中有着广泛的运用,其中最著名的一个项目就是Babel,Babel是一个被广泛运用的代码转换器,可以将es6代码转换为es5代码,这意味着我们现在就可以用es6编写程序,而不用担心现有环境是否支持es6.Bebel的工作原理就是将es6源码转换为AST,然后再将ES6的AST转换为ES5语法的AST,最后利用ES5的AST生成javaSript源码

除了Babel之外,还有ESLint也是使用AST.ESLint是一个用来检查javascript编写规范的插件,其检测流程也是需要将源码转换为AST来检查代码规范化的问题

现在你知道了什么是AST以及它的一些应用,那接下来我们看看AST是如何生成的.通常,生成AST需要经过两个阶段.

第一阶段是分词(tokenize),又称为词法分析,其作用是将一行行的源码拆解成一个个token,所谓token,指的是语法上不可能再分的最小的单个字符或者字符串,

**第二阶段是解析(Parse)**又称为语法分析,其作用是将上一步生成的token数据,根据语法规则转为AST,如果源码符合语法规则,这一步就会顺利完成.但如果源码存在语法错误,这一步就会终止,并抛出一个语法错误.

这就是AST的生成过程,先分词,再解析

生成字节码

有了AST之后,那接下来的第二步就是解释器登场了,他会个根据AST生成字节码,并且逐行解释执行字节码
其实一开始V8并没有字节码,而是直接将AST转换为机器码,由于执行机器码的效率是非常高效的,所以这种方式在发布后的一段时间内运行效果是非常好的.但是随着chrome在手机上的广泛普及,特别是运行在512M内存的手机上,内存占用的问题也暴露了出来,因为V8要消耗大量的内存来存放转换后的机器码.为了解决内存占用问题,V8天涯不等于大幅重构了引擎架构,引入字节码,并且抛弃了之前的编译器,最终花了将近4年的时间,实现了现在的这套架构.

那什么是字节码呢?为什么要引入字节码就能解决内存占用问题呢?

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

高级代码,字节码,机器码三者的内存占用是逐渐增大的,字节码作为一个中间码能减少系统的内存使用

3.执行代码

生成字节码后,接下来就要进入执行阶段了.
通常,如果有一段第一次执行的字节码,解释器会逐条解释执行,在执行字节码的过程中,如果发现有热点代码,比如一段代码被重复执行很多次,这种代码就被称为热点代码,那么后台的编译器就会把该热点的字节码编译为高效的机器码,然后再次执行到这段代码时,只需要执行编译后的机器码就可以了,这样就大大提升了代码的执行效率

V8的解释器和编译器的取名也很有意思…解释器lgnition是点火器的意思,编译器TurboFan是涡轮增压的意思,寓意着代码启动时通过点火器慢慢发动,一旦启动,涡轮增压介入,其执行效率随着执行时间越来越搞笑,因为热点代码都被编译器TurboFan转换为了机器码,直接执行机器码就省去了字节码翻译为机器码的过程

其实字节码配合解释器和编译器时最近一段时间很火的技术,比如java和python的虚拟机也都是基于这种技术实现的,我们把这种技术称为即时编译(JIT).具体到V8,就是指解释器lgnition在解释执行字节码的同时,收集代码信息,当它发现某一部分代码变热了以后,TuboFan编译器闪亮登场,把热点的字节码转换为机器码,并把转换后的机器码保存起来,以备下次使用

对于JavaScript工作引擎,除了使用字节码+jit技术为外,苹果的SquirrelFish Extreme和Mozilla的SpiderMonKey也都使用了该技术

这么多语言的工作引擎都使用了字节码+JIT技术,因为理解JIT这套工作机制还是很有必要的
我们大致捋一下流程

JavaScript Code->字节码->hot?JIT编译器一次编译代码:解释器逐行解释->机器码 

JavaScript的性能优化

到这里我们已经了解了V8是如何执行一段代码的,在过去几年里,JavaScript的性能得到了大幅提升,这得益于V8团队的解释器和编译器的不断改进和优化

虽然在V8诞生之初,也出现过一系列针对V8而专门优化JavaScript性能的方案,比如隐藏类,内联缓存等概念都是那时候提出来的.不过随着V8的架构调整,也越来越不需要这些微优化策略了,相反,对于优化JavaScript执行效率,你应该将优化的中心聚焦在单次脚本的执行时间和脚本的网络下载上,主要关注一下三点内容

  • 提升单词脚本的执行效率,避免JavaScript的长任务霸占主线程过久,使得页面快速响应交互
  • 避免大的内联脚本,因为在解析HTML的过程中,解析和编译也会占用主线程
  • 减少JavaScript文件的容量,因为更小的文件会提示下载速度,并且占用更低的内存(这里webpack,Vite者Esbuild会帮你构建好的)

好了 今天就讲到这里 这章内容可能对于多数人来说比较抽象,如果理解上有困难或者说更深入理解的化,可以自己买些书看看,包括编译原理等等 累了 到这吧

管理员
0
0
0
分享
上一篇: notep 运行php,Notepad++ 运行脚本快捷键设置,notepad脚本_PHP教程
下一篇: 传奇服务器系统版本,[资料]服务端详细解释
评论
历史记录
回顶部
浏览时间 游戏名称 游戏IP 开区网址
注册GM1论坛账号
  • 上传头像
注册

已有账号,

微信扫码登录
重置密码
重置密码

注册

绑定关联手机号
关联手机号