我们经常说写好的js代码被运行需要经过解析、执行阶段,那里面详细的过程是怎么样的吗,我们现在详细探究下。
一、来个案例演示完整流程
我们将使用以下简单的 JavaScript 代码作为示例,详细讲解js从解析到执行的完整过程。
function sayHello() {
console.log("Hello, world!");
}
sayHello();
1. 词法分析(Lexical Analysis)
词法分析将源代码分解成标记(tokens)。标记是代码的最小单元,例如关键字、标识符、操作符和字面量。 产物:标记(Tokens)
[
{ type: 'Keyword', value: 'function' },
{ type: 'Identifier', value: 'sayHello' },
{ type: 'Punctuation', value: '(' },
{ type: 'Punctuation', value: ')' },
{ type: 'Punctuation', value: '{' },
{ type: 'Identifier', value: 'console' },
{ type: 'Punctuation', value: '.' },
{ type: 'Identifier', value: 'log' },
{ type: 'Punctuation', value: '(' },
{ type: 'String', value: 'Hello, world!' },
{ type: 'Punctuation', value: ')' },
{ type: 'Punctuation', value: ';' },
{ type: 'Punctuation', value: '}' },
{ type: 'Identifier', value: 'sayHello' },
{ type: 'Punctuation', value: '(' },
{ type: 'Punctuation', value: ')' },
{ type: 'Punctuation', value: ';' }
]
2. 语法分析(Syntax Analysis)
语法分析将标记转换为抽象语法树(AST)。AST 是代码的结构化表示,展示了代码的语法结构。
产物:抽象语法树(AST)
Program
├── FunctionDeclaration
│ ├── Identifier (name: "sayHello")
│ ├── BlockStatement
│ └── ExpressionStatement
│ └── CallExpression
│ ├── MemberExpression
│ │ ├── Identifier (name: "console")
│ │ └── Identifier (name: "log")
│ └── Literal (value: "Hello, world!")
└── ExpressionStatement
└── CallExpression
├── Identifier (name: "sayHello")
└── Arguments
3. 生成字节码(Bytecode Generation)
JavaScript 引擎将 AST 转换为字节码。字节码是介于源代码和机器码之间的一种中间表示形式,便于解释器执行。
产物:字节码(示例) 以下是 V8 引擎生成的字节码示例(伪代码表示):
[generated bytecode for function: sayHello]
Parameter count 1
Frame size 8
0x12345678 @ 0 : a7 StackCheck
0x12345679 @ 1 : 32 00 LdaSmi [0]
0x1234567b @ 3 : 1d fb Star r0
0x1234567d @ 5 : 65 fb 00 LdaGlobal [0], [0]
0x12345680 @ 8 : 1d fa Star r1
0x12345682 @ 10 : 65 fa 01 LdaGlobal [1], [1]
0x12345685 @ 13 : 1d f9 Star r2
0x12345687 @ 15 : 13 f9 02 LdaNamedProperty r2, [2]
0x1234568a @ 18 : 1d f8 Star r3
0x1234568c @ 20 : 13 f8 03 LdaNamedProperty r3, [3]
0x1234568f @ 23 : 1d f7 Star r4
0x12345691 @ 25 : 89 f7 01 00 CallProperty1 r4, r1, [1]
0x12345695 @ 29 : a9 Return
Constant pool (size = 4)
0x12345678: [FixedArray] in OldSpace
- map = 0x12345678 <Map(HOLEY_ELEMENTS)>
- length: 4
0: 0x12345678 <String[6]: #console>
1: 0x12345678 <String[3]: #log>
2: 0x12345678 <String[12]: #Hello, world!>
3: 0x12345678 <String[8]: #sayHello>
4. 执行(Execution)
JavaScript 引擎解释并执行字节码。执行阶段包括调用函数、计算表达式和处理控制流。
产物:执行结果
Hello, world!
二、详细步骤总结
1.词法分析:将源代码分解成标记。
输入:function sayHello() { console.log("Hello, world!"); } sayHello();
输出:标记数组。
2.语法分析:将标记转换为抽象语法树
输入:标记数组。
输出:抽象语法树(AST)。
3.生成字节码:将抽象语法树转换为字节码
输入:抽象语法树。
输出:字节码。
4.执行:解释并执行字节码
输入:字节码。
输出:执行结果(控制台输出 "Hello, world!"。
通过这些步骤,JavaScript 引擎将源代码解析并执行,最终输出结果。