注册

V8开篇:V8是如何运行JavaScript(let a = 1)代码的?


我们知道,机器是不能直接理解我们平常工作或者自己学习的代码的。所以,在执行程序之前,需要将代码翻译成机器能读懂的机器语言。按语言的执行流程,可以把计算机语言划分为编译型语言和解释型语言:



编译型语言:在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果。




解释型语言:需要将代码转换成机器码,和编译型语言的区别在于运行时需要转换。解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。



Java 和 C++ 等语言都是编译型语言,而 JavaScript 是解释性语言,它整体的执行速度会略慢于编译型的语言。V8 是众多JavaScript引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。


1.运行的整体过程


未命名文件 (4).png


2.英译汉翻译的过程


比如我们看到了google V8官网的一篇英文文章 v8.dev/blog/faster…,在阅读的过程中,可以就是要对每一个单词进行解析翻译成中文,然后多个单词进行语法的解析,再通过对整句话进行整个语句进行解析,那么这句话就翻译结束了。


下面我们就举例一句英文的翻译过程:I am a programmer。


  • 1、首先对输入的字符串I am a programmer。进行拆分便会拆分成 I am a programmer


相当于词法分析




  • 2、I 是一个主语, am 是一个谓语, a是一个形容词, programmer是个名词, 标点符号。



  • 3、I的意思, am的意思, a一个的意思, programmer程序员的意思, 句号的意思。




2和3一起相当于语法分析



  • 4、对3中的语法分析进行拼接处理:我是一个程序员。当然这是非常简单的一个英译汉,一篇文章的话,就会复杂一些了。


相当于语义分析



3.V8运行的整个过程


3.1.准备一段JavaScript源代码


let a = 10

3.2.词法分析:


一段源代码,就是一段字符串。编译器识别源代码的第一步就是要进行分词,将源代码拆解成一个个的token。所谓的token,就是不可再分的单个字符或者字符串。


3.3.token


通过 esprima.org/demo/parse.… 可以查看生成的tokens,也就是上面那段源代码生成的所有token。


Token类别: 关键字、标识符、字面量、操作符、数据类型(String、Numeric)等


image.png


3.4.语法分析


将上一步生成的 token 数据,根据语法规则转为 AST。通过astexplorer.net 可以查看生成AST抽象语法树。


3.5.AST


生成的AST如下图所示,生成过程就是先分词(词法分析),再解析(语法分析)


image.png
当然你也可以查看生成的AST的JSON结构


{
"type": "Program",
"start": 0,
"end": 9,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 9,
"declarations": [
{
"type": "VariableDeclarator",
"start": 4,
"end": 9,
"id": {
"type": "Identifier",
"start": 4,
"end": 5,
"name": "a"
},
"init": {
"type": "Literal",
"start": 8,
"end": 9,
"value": 1,
"raw": "1"
}
}
],
"kind": "let"
}
],
"sourceType": "module"
}

同样我在本地下载了v8,直接用v8来查看AST


v8-debug  --print-ast hello.js

image.png


3.6.解释器


解释器会将AST生成字节码,生成字节码的过程也就是对AST抽象语法树进行遍历循环,并进行语义分析


3.7.字节码


在最开始的V8引擎中是没有字节码,是直接将AST转换生成为机器码。这种架构存在的问题就是内存消耗特别大,尤其是在移动设备上,编译出来的机器码占了整个chorme浏览器的三分之一,这样为代码运行时留下的内存就更小了。
于是后来在V8中加入了Ignition 解释器,引入字节码,主要就是为了减少内存消耗。
本地可以使用V8命令行查看生成的字节码


v8-debug  --print-bytecode hello.js

image.png


3.8.热点代码


首先判断字节码是否为热点代码。通常第一次执行的字节码,Ignition 解释器会逐条解释执行。在执行的过程中,如果发现是热点代码,比如for 循环中的代码被执行了多次,这种就称之为热点代码。那么后台的TurboFan就会把该段热点代码编译为高效的机器码,然后再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了, 这样就大大提升了代码的执行效率。


3.9.编译器


TurboFan编译器也可以说是JIT的即时编译器,也可以说是优化编译器。



Ignition 解释器: 可以将AST生成字节码,还可以解释执行字节码。



4、总结


  • 了解V8整个的运行机制
  • 学习JavaScript到底是怎么运行的
  • 对日后编写JavaScript代码有非常多的好处
  • 看完学习了,能提升我们的技术水平
  • 对于日后遇到问题,能够从底层去思考问题出在那里,更快速的定位和解决问题
  • 真的非常熟悉了,可以自己开发一门新的语言
 

0 个评论

要回复文章请先登录注册