js代码被解析执行的过程到底是怎么样的?3分钟看懂

296 阅读3分钟

我们经常说写好的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 引擎将源代码解析并执行,最终输出结果。