返回

拆解JS语法的神奇世界——认识词法分析与抽象语法树

前端

如今, JavaScript 已经成为前端开发中不可或缺的一部分,越来越多的开发人员开始深入学习其底层原理,以便更好地掌握和使用它。在学习JS的过程中,词法分析和抽象语法树这两个概念经常被提及,但很多人对它们的作用和实现原理并不十分清楚。

首先,让我们从词法分析器开始。词法分析器是一个将JS代码拆分成可以进行编译的代码的程序。它将代码中的字符串、数字、标识符和其他符号分割成单独的词法单元(也称为标记或令牌)。在JS中,词法单元可以是(如var、if、else等)、运算符(如+、-、*、/等)、标点符号(如分号、逗号、括号等)、标识符(即变量或函数名)等。词法分析器的输出是一个标记流,其中包含了所有已识别的词法单元以及它们的类型和位置信息。

接下来,抽象语法树(AST)登场了。AST是一种将标记流组织成树状结构的数据结构,它可以清晰地表示代码的语法结构和语义。AST的根节点通常是整个程序的入口函数,每个子节点表示一个语句或表达式。通过AST,我们可以很容易地理解代码的执行流程和逻辑结构。

举个例子,让我们来看看下面的代码:

var a = 2;
if (a > 1) {
  console.log('a is greater than 1');
} else {
  console.log('a is not greater than 1');
}

这个代码的作用是判断变量a是否大于1,并根据结果输出相应的日志。词法分析器会将这段代码拆分成如下所示的标记流:

[
  { type: 'keyword', value: 'var', pos: 0 },
  { type: 'identifier', value: 'a', pos: 4 },
  { type: 'operator', value: '=', pos: 6 },
  { type: 'number', value: '2', pos: 8 },
  { type: 'semicolon', value: ';', pos: 9 },
  { type: 'keyword', value: 'if', pos: 11 },
  { type: 'parenthesis_open', value: '(', pos: 13 },
  { type: 'identifier', value: 'a', pos: 14 },
  { type: 'operator', value: '>', pos: 16 },
  { type: 'number', value: '1', pos: 18 },
  { type: 'parenthesis_close', value: ')', pos: 19 },
  { type: 'brace_open', value: '{', pos: 21 },
  { type: 'keyword', value: 'console', pos: 23 },
  { type: 'dot', value: '.', pos: 30 },
  { type: 'identifier', value: 'log', pos: 31 },
  { type: 'parenthesis_open', value: '(', pos: 34 },
  { type: 'string', value: 'a is greater than 1', pos: 35 },
  { type: 'parenthesis_close', value: ')', pos: 54 },
  { type: 'semicolon', value: ';', pos: 55 },
  { type: 'brace_close', value: '}', pos: 57 },
  { type: 'keyword', value: 'else', pos: 59 },
  { type: 'brace_open', value: '{', pos: 64 },
  { type: 'keyword', value: 'console', pos: 66 },
  { type: 'dot', value: '.', pos: 73 },
  { type: 'identifier', value: 'log', pos: 74 },
  { type: 'parenthesis_open', value: '(', pos: 77 },
  { type: 'string', value: 'a is not greater than 1', pos: 78 },
  { type: 'parenthesis_close', value: ')', pos: 97 },
  { type: 'semicolon', value: ';', pos: 98 },
  { type: 'brace_close', value: '}', pos: 99 }
]

然后,AST会将标记流转换为以下所示的树状结构:

Program
  VariableDeclaration
    Identifier: a
    Initializer:
      Number: 2
  IfStatement
    Test:
      BinaryExpression
        Left:
          Identifier: a
        Operator: >
        Right:
          Number: 1
    Consequent:
      BlockStatement
        ExpressionStatement
          Expression:
            CallExpression
              Callee:
                MemberExpression
                  Object:
                    Identifier: console
                  Property:
                    Identifier: log
              Arguments:
                String: a is greater than 1
    Alternate:
      BlockStatement
        ExpressionStatement
          Expression:
            CallExpression
              Callee:
                MemberExpression
                  Object:
                    Identifier: console
                  Property:
                    Identifier: log
              Arguments:
                String: a is not greater than 1

通过AST,我们可以清晰地看到这段代码的执行流程:首先声明变量a并初始化为2,然后判断a是否大于1,如果是,则输出"a is greater than 1",否则输出"a is not greater than 1"。

词法分析器和抽象语法树是JS编译器中非常重要的两个组成部分,它们共同作用,将JS代码转换成计算机可以理解的机器码。掌握这些知识,不仅可以帮助我们更好地理解JS的编译过程,还能为我们深入学习JS的语法和运行机制打下坚实的基础。