美文网首页让前端飞
13 个例子快速了解JS抽象语法树

13 个例子快速了解JS抽象语法树

作者: Pines_Cheng | 来源:发表于2018-07-19 21:03 被阅读2次

    更多博客文章,欢迎 Star Github/Blog

    Javascript 代码的解析(Parse )步骤分为两个阶段:**词法分析(Lexical Analysis) **语法分析(Syntactic Analysis)。这个步骤接收代码并输出 抽象语法树,亦称 AST。

    随着 Babel 的生态越来越完善,我们通常会使用 Babel 来帮助我们分析代码的解析过程。Babel 使用一个基于 ESTree 并修改过的 AST,它的内核说明文档可以在 [这里](https://github. com/babel/babel/blob/master/doc/ast/spec. md) 找到。

    在分析 Javascript 的 AST 过程中,借助于工具 AST Explorer 能帮助我们对 AST 节点有一个更好的感性认识。

    为了帮助大家更好的结合实例分析,了解核心的 Babylon AST node types 组成,这里列举了 13 个常用例子,并分别列出了对应的 AST 节点及详细的 node types 解析。

    以下所有的代码的 AST 全部基于 Babylon7

    变量声明

    代码

    let a  = 'hello'
    

    AST

    image

    VariableDeclaration

    变量声明,kind 属性表示是什么类型的声明,因为 ES6 引入了 const/let
    declarations 表示声明的多个描述,因为我们可以这样:let a = 1, b = 2;

    interface VariableDeclaration <: Declaration {
        type: "VariableDeclaration";
        declarations: [ VariableDeclarator ];
        kind: "var";
    }
    

    VariableDeclarator

    变量声明的描述,id 表示变量名称节点,init 表示初始值的表达式,可以为 null

    interface VariableDeclarator <: Node {
        type: "VariableDeclarator";
        id: Pattern;
        init: Expression | null;
    } 
    

    Identifier

    标识符,我觉得应该是这么叫的,就是我们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:

    interface Identifier <: Expression, Pattern {
        type: "Identifier";
        name: string;
    }
    

    一个标识符可能是一个表达式,或者是解构的模式(ES6 中的解构语法)。我们等会会看到 ExpressionPattern 相关的内容的。

    Literal

    字面量,这里不是指 [] 或者 {} 这些,而是本身语义就代表了一个值的字面量,如 1“hello”, true 这些,还有正则表达式(有一个扩展的 Node 来表示正则表达式),如 /\d?/。我们看一下文档的定义:

    interface Literal <: Expression {
        type: "Literal";
        value: string | boolean | null | number | RegExp;
    }
    

    value 这里即对应了字面量的值,我们可以看出字面量值的类型,字符串,布尔,数值,null 和正则。

    二元运算表达式

    代码

    let a = 3+4
    

    AST

    image

    BinaryExpression

    二元运算表达式节点,leftright 表示运算符左右的两个表达式,operator 表示一个二元运算符。

    interface BinaryExpression <: Expression {
        type: "BinaryExpression";
        operator: BinaryOperator;
        left: Expression;
        right: Expression;
    }
    

    BinaryOperator

    二元运算符,所有值如下:

    enum BinaryOperator {
        "==" | "!=" | "===" | "!=="
             | "<" | "<=" | ">" | ">="
             | "<<" | ">>" | ">>>"
             | "+" | "-" | "*" | "/" | "%"
             | "|" | "^" | "&" | "in"
             | "instanceof"
    }
    

    赋值表达式

    代码

    这个例子会稍微复杂一点,涉及到的 Node 类型比较多。

        this.state = {date: new Date()};
    

    AST

    image

    ExpressionStatement

    表达式语句节点,a = a + 1 或者 a++ 里边会有一个 expression 属性指向一个表达式节点对象(后边会提及表达式)。

    interface ExpressionStatement <: Statement {
        type: "ExpressionStatement";
        expression: Expression;
    }
    

    AssignmentExpression

    赋值表达式节点,operator 属性表示一个赋值运算符,leftright 是赋值运算符左右的表达式。

    interface AssignmentExpression <: Expression {
        type: "AssignmentExpression";
        operator: AssignmentOperator;
        left: Pattern | Expression;
        right: Expression;
    }
    
    AssignmentOperator

    赋值运算符,所有值如下:(常用的并不多)

    enum AssignmentOperator {
        "=" | "+=" | "-=" | "*=" | "/=" | "%="
            | "<<=" | ">>=" | ">>>="
            | "|=" | "^=" | "&="
    }
    

    MemberExpression

    成员表达式节点,即表示引用对象成员的语句,object 是引用对象的表达式节点,property 是表示属性名称,computed 如果为 false,是表示 . 来引用成员,property 应该为一个 Identifier 节点,如果 computed 属性为 true,则是 [] 来进行引用,即 property 是一个 Expression 节点,名称是表达式的结果值。

    interface MemberExpression <: Expression, Pattern {
        type: "MemberExpression";
        object: Expression;
        property: Expression;
        computed: boolean;
    }
    

    ThisExpression

    表示 this

    interface ThisExpression <: Expression {
        type: "ThisExpression";
    }
    

    ObjectExpression

    对象表达式节点,property 属性是一个数组,表示对象的每一个键值对,每一个元素都是一个属性节点。

    interface ObjectExpression <: Expression {
        type: "ObjectExpression";
        properties: [ Property ];
    }
    

    Property

    对象表达式中的属性节点。key 表示键,value 表示值,由于 ES5 语法中有 get/set 的存在,所以有一个 kind 属性,用来表示是普通的初始化,或者是 get/set

    interface Property <: Node {
        type: "Property";
        key: Literal | Identifier;
        value: Expression;
        kind: "init" | "get" | "set";
    }
    

    NewExpression

    new 表达式。

    interface NewExpression <: CallExpression {
        type: "NewExpression";
    }
    

    函数调用表达式

    代码

        console.log(`Hello ${name}`)
    

    AST

    image

    CallExpression

    函数调用表达式,即表示了 func(1, 2) 这一类型的语句。callee 属性是一个表达式节点,表示函数,arguments 是一个数组,元素是表达式节点,表示函数参数列表。

    interface CallExpression <: Expression {
        type: "CallExpression";
        callee: Expression;
        arguments: [ Expression ];
    }
    

    TemplateLiteral

    interface TemplateLiteral <: Expression {
      type: "TemplateLiteral";
      quasis: [ TemplateElement ];
      expressions: [ Expression ];
    }
    

    TemplateElement

    interface TemplateElement <: Node {
      type: "TemplateElement";
      tail: boolean;
      value: {
        cooked: string | null;
        raw: string;
      };
    }
    

    箭头函数

    代码

    i => i++
    

    AST

    image

    ArrowFunctionExpression

    箭头函数表达式。

    interface ArrowFunctionExpression <: Function, Expression {
      type: "ArrowFunctionExpression";
      body: BlockStatement | Expression;
      expression: boolean;
    }
    

    UpdateExpression

    update 运算表达式节点,即 ++/--,和一元运算符类似,只是 operator 指向的节点对象类型不同,这里是 update 运算符。

    interface UpdateExpression <: Expression {
        type: "UpdateExpression";
        operator: UpdateOperator;
        argument: Expression;
        prefix: boolean;
    }
    
    UpdateOperator

    update 运算符,值为 ++--,配合 update 表达式节点的 prefix 属性来表示前后。

    enum UpdateOperator {
        "++" | "--"
    }
    

    函数声明

    代码

    function Hello(name = 'Lily'){
        
    }
    

    AST

    image

    FunctionDeclaration

    函数声明,和之前提到的 Function 不同的是,id 不能为 null

    interface FunctionDeclaration <: Function, Declaration {
        type: "FunctionDeclaration";
        id: Identifier;
    }
    

    AssignmentPattern

    interface AssignmentPattern <: Pattern {
      type: "AssignmentPattern";
      left: Pattern;
      right: Expression;
    }
    

    BlockStatement

    块语句节点,举个例子:if (...) { // 这里是块语句的内容 },块里边可以包含多个其他的语句,所以有一个 body 属性,是一个数组,表示了块里边的多个语句。

    interface BlockStatement <: Statement {
        type: "BlockStatement";
        body: [ Statement ];
    }
    

    类声明

    代码

    class Clock extends Component{
        render(){
        }
    }
    

    AST

    [站外图片上传中...(image-597960-1532005358414)]

    Classes

    interface Class <: Node {
      id: Identifier | null;
      superClass: Expression | null;
      body: ClassBody;
      decorators: [ Decorator ];
    }
    

    ClassBody

    interface ClassBody <: Node {
      type: "ClassBody";
      body: [ ClassMethod | ClassPrivateMethod | ClassProperty | ClassPrivateProperty ];
    }
    

    ClassMethod

    interface ClassMethod <: Function {
      type: "ClassMethod";
      key: Expression;
      kind: "constructor" | "method" | "get" | "set";
      computed: boolean;
      static: boolean;
      decorators: [ Decorator ];
    }
    

    if 语句

    代码

    if(a === 0){
    }
    

    AST

    image

    IfStatement

    if 语句节点,很常见,会带有三个属性,test 属性表示 if (...) 括号中的表达式。

    consequent 属性是表示条件为 true 时的执行语句,通常会是一个块语句。

    alternate 属性则是用来表示 else 后跟随的语句节点,通常也会是块语句,但也可以又是一个 if 语句节点,即类似这样的结构:
    if (a) { //... } else if (b) { // ... }
    alternate 当然也可以为 null

    interface IfStatement <: Statement {
        type: "IfStatement";
        test: Expression;
        consequent: Statement;
        alternate: Statement | null;
    }
    

    switch 语句

    代码

    switch(num){
      case 0:
        x = 'Sunday'
        break;
      default:
        x = 'Weekday'
    }
    

    AST

    image

    SwitchStatement

    switch 语句节点,有两个属性,discriminant 属性表示 switch 语句后紧随的表达式,通常会是一个变量,cases 属性是一个 case 节点的数组,用来表示各个 case 语句。

    interface SwitchStatement <: Statement {
        type: "SwitchStatement";
        discriminant: Expression;
        cases: [ SwitchCase ];
    }
    

    SwitchCase

    switchcase 节点。test 属性代表这个 case 的判断表达式,consequent 则是这个 case 的执行语句。

    test 属性是 null 时,则是表示 default 这个 case 节点。

    interface SwitchCase <: Node {
        type: "SwitchCase";
        test: Expression | null;
        consequent: [ Statement ];
    }
    

    for 语句

    代码

    for (var i = 0; i < 9; i++) {
    }
    

    AST

    image

    ForStatement

    for 循环语句节点,属性 init/test/update 分别表示了 for 语句括号中的三个表达式,初始化值,循环判断条件,每次循环执行的变量更新语句(init 可以是变量声明或者表达式)。这三个属性都可以为 null,即 for(;;){}
    body 属性用以表示要循环执行的语句。

    interface ForStatement <: Statement {
        type: "ForStatement";
        init: VariableDeclaration | Expression | null;
        test: Expression | null;
        update: Expression | null;
        body: Statement;
    }
    

    模块引入

    代码

    import React from 'react'
    

    AST

    [站外图片上传中...(image-7f97a0-1532005358414)]

    ImportDeclaration

    模块声明。

    interface ImportDeclaration <: ModuleDeclaration {
      type: "ImportDeclaration";
      specifiers: [ ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier ];
      source: Literal;
    }
    

    ImportDefaultSpecifier

    interface ImportDefaultSpecifier <: ModuleSpecifier {
      type: "ImportDefaultSpecifier";
    }
    

    模块导出

    代码

    export default Clock
    

    AST

    image

    ExportDefaultDeclaration

    interface OptFunctionDeclaration <: FunctionDeclaration {
      id: Identifier | null;
    }
    
    interface OptClasDeclaration <: ClassDeclaration {
      id: Identifier | null;
    }
    
    interface ExportDefaultDeclaration <: ModuleDeclaration {
      type: "ExportDefaultDeclaration";
      declaration: OptFunctionDeclaration | OptClassDeclaration | Expression;
    }
    

    JSX render 方法

    代码:

      render() {
        return (
          <div>
            <h1>Hello, world!</h1>
            <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
          </div>
        );
      }
    

    AST

    image

    参考

    相关文章

      网友评论

        本文标题:13 个例子快速了解JS抽象语法树

        本文链接:https://www.haomeiwen.com/subject/bjdxmftx.html