蓝狮注册登陆V8 是如何执行 JavaScript 代码的?

大家好,我是 CUGGZ。

今天来分享 V8 引擎执行 JavaScript 的原理和垃圾回收机制。

  1. JS 代码执行过程
    在说V8的执行JavaScript代码的机制之前,蓝狮注册开户我们先来看看编译型和解释型语言的区别。

(1)编译型语言和解释型语言
我们知道,机器是不能直接理解代码的。所以,在执行程序之前,需要将代码翻译成机器能读懂的机器语言。按语言的执行流程,可以把计算机语言划分为编译型语言和解释型语言:

编译型语言:在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果;
解释型语言:需要将代码转换成机器码,和编译型语言的区别在于运行时需要转换。解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。
Java 和 C++ 等语言都是编译型语言,而 JavaScript 是解释性语言,它整体的执行速度会略慢于编译型的语言。V8 是众多浏览器的 JS 引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。

编译型语言和解释器语言代码执行的具体流程如下:

两者的执行流程如下:

在编译型语言的编译过程中,编译器首先会依次对源代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后再生成处理器能够理解的机器码。如果编译成功,将会生成一个可执行的文件。但如果编译过程发生了语法或者其他的错误,那么编译器就会抛出异常,最后的二进制文件也不会生成成功。

在解释型语言的解释过程中,同样解释器也会对源代码进行词法分析、语法分析,并生成抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后再根据字节码来执行程序、输出结果。

(2) V8 执行代码过程
V8 在执行过程用到了解释器和编译器。 其执行过程如下:

Parse 阶段:V8 引擎将 JS 代码转换成 AST(抽象语法树);
Ignition 阶段:解释器将 AST 转换为字节码,解析执行字节码也会为下一个阶段优化编译提供需要的信息;
TurboFan 阶段:编译器利用上个阶段收集的信息,将字节码优化为可以执行的机器码;
Orinoco 阶段:垃圾回收阶段,将程序中不再使用的内存空间进行回收。
这里前三个步骤是JavaScript的执行过程,最后一步是垃圾回收的过程。下面就先来看看V8 执行 JavaScript的过程。

① 生成抽象语法树
这个过程就是将源代码转换为抽象语法树(AST),并生成执行上下文,执行上下文就是代码在执行过程中的环境信息。

将 JS 代码解析成 AST主要分为两个阶段:

词法分析:这个阶段会将源代码拆成最小的、不可再分的词法单元,称为 token。比如代码 var a = 1;通常会被分解成 var 、a、=、1、; 这五个词法单元。代码中的空格在 JavaScript 中是直接忽略的,简单来说就是将 JavaScript 代码解析成一个个令牌(Token)。
语法分析:这个过程是将上一步生成的 token 数据,根据语法规则转为 AST。如果源码符合语法规则,这一步就会顺利完成。如果源码存在语法错误,这一步就会终止,并抛出一个语法错误,简单来说就是将令牌组装成一棵抽象的语法树(AST)。
通过词法分析会对代码逐个字符进行解析,生成类似下面结构的令牌(Token),这些令牌类型各不相同,有关键字、标识符、符号、数字等。代码 var a = 1;会转化为下面这样的令牌:

Keyword(var)
Identifier(name)
Punctuator(=)
Number(1)
语法分析阶段会用令牌生成一棵抽象语法树,生成树的过程中会去除不必要的符号令牌,然后按照语法规则来生成。下面来看两段代码:

// 第一段代码
var a = 1;
// 第二段代码
function sum (a,b) {
return a + b;
}
将这两段代码分别转换成 AST 抽象语法树之后返回的 JSON 如下:

第一段代码,编译后的结果:
{
“type”: “Program”,
“start”: 0,
“end”: 10,
“body”: [
{
“type”: “VariableDeclaration”,
“start”: 0,
“end”: 10,
“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”: “var”
}
],
“sourceType”: “module”
}
它的结构大致如下:

第二段代码,蓝狮注册登陆编译出来的结果:
{
“type”: “Program”,
“start”: 0,
“end”: 38,
“body”: [
{
“type”: “FunctionDeclaration”,
“start”: 0,
“end”: 38,
“id”: {
“type”: “Identifier”,
“start”: 9,
“end”: 12,
“name”: “sum”
},
“expression”: false,
“generator”: false,
“async”: false,
“params”: [
{
“type”: “Identifier”,
“start”: 14,
“end”: 15,
“name”: “a”
},
{
“type”: “Identifier”,
“start”: 16,
“end”: 17,
“name”: “b”
}
],
“body”: {
“type”: “BlockStatement”,
“start”: 19,
“end”: 38,
“body”: [
{
“type”: “ReturnStatement”,
“start”: 23,
“end”: 36,
“argument”: {
“type”: “BinaryExpression”,
“start”: 30,
“end”: 35,
“left”: {
“type”: “Identifier”,
“start”: 30,
“end”: 31,
“name”: “a”
},
“operator”: “+”,
“right”: {
“type”: “Identifier”,
“start”: 34,
“end”: 35,
“name”: “b”
}
}
}
]
}
}
],
“sourceType”: “module”
}

0 Comments
Leave a Reply