V8开篇:V8是如何运行JavaScript(let a = 1)代码的?
我们知道,机器是不能直接理解我们平常工作或者自己学习的代码的。所以,在执行程序之前,需要将代码翻译成机器能读懂的机器语言。按语言的执行流程,可以把计算机语言划分为编译型语言和解释型语言:
编译型语言:在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果。
解释型语言:需要将代码转换成机器码,和编译型语言的区别在于运行时需要转换。解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。
Java 和 C++ 等语言都是编译型语言,而 JavaScript 是解释性语言,它整体的执行速度会略慢于编译型的语言。V8 是众多JavaScript引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。
1.运行的整体过程
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)等
3.4.语法分析
将上一步生成的 token 数据,根据语法规则转为 AST。通过astexplorer.net 可以查看生成AST抽象语法树。
3.5.AST
生成的AST如下图所示,生成过程就是先分词(词法分析),再解析(语法分析)
当然你也可以查看生成的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
3.6.解释器
解释器会将AST生成字节码,生成字节码的过程也就是对AST抽象语法树进行遍历循环,并进行语义分析
3.7.字节码
在最开始的V8引擎中是没有字节码,是直接将AST转换生成为机器码。这种架构存在的问题就是内存消耗特别大,尤其是在移动设备上,编译出来的机器码占了整个chorme浏览器的三分之一,这样为代码运行时留下的内存就更小了。
于是后来在V8中加入了Ignition 解释器,引入字节码,主要就是为了减少内存消耗。
本地可以使用V8命令行查看生成的字节码
v8-debug --print-bytecode hello.js
3.8.热点代码
首先判断字节码是否为热点代码。通常第一次执行的字节码,Ignition 解释器会逐条解释执行。在执行的过程中,如果发现是热点代码,比如for 循环中的代码被执行了多次
,这种就称之为热点代码。那么后台的TurboFan就会把该段热点代码编译为高效的机器码,然后再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了, 这样就大大提升了代码的执行效率。
3.9.编译器
TurboFan编译器也可以说是JIT的即时编译器,也可以说是优化编译器。
Ignition 解释器: 可以将AST生成字节码,还可以解释执行字节码。
4、总结
- 了解V8整个的运行机制
- 学习JavaScript到底是怎么运行的
- 对日后编写JavaScript代码有非常多的好处
- 看完学习了,能提升我们的技术水平
- 对于日后遇到问题,能够从底层去思考问题出在那里,更快速的定位和解决问题
- 真的非常熟悉了,可以自己开发一门新的语言