JavaScript之AST抽象语法树

在github上看到一个相关题目:

a.b.c.d和a[‘b’][‘c’][‘d’],哪个性能更高

别看这题,题目上每个字都能看懂,但是里面涉及到的知识,暗藏杀鸡 这题要往深处走,会涉及ast抽象语法树、编译原理、v8内核对原生js实现问题

然后用ast的方式来解构代码:
对应于a.b.c.d:

    {
      "type": "Program",
      "start": 0,
      "end": 7,
      "body": [
        {
          "type": "ExpressionStatement",
          "start": 0,
          "end": 7,
          "expression": {
            "type": "MemberExpression",
            "start": 0,
            "end": 7,
            "object": {
              "type": "MemberExpression",
              "start": 0,
              "end": 5,
              "object": {
                "type": "MemberExpression",
                "start": 0,
                "end": 3,
                "object": {
                  "type": "Identifier",
                  "start": 0,
                  "end": 1,
                  "name": "a"
                },
                "property": {
                  "type": "Identifier",
                  "start": 2,
                  "end": 3,
                  "name": "b"
                },
                "computed": false
              },
              "property": {
                "type": "Identifier",
                "start": 4,
                "end": 5,
                "name": "c"
              },
              "computed": false
            },
            "property": {
              "type": "Identifier",
              "start": 6,
              "end": 7,
              "name": "d"
            },
            "computed": false
          }
        }
      ],
      "sourceType": "module"
    }

对应于a[‘b’][‘c’][‘d’]:

 {
      "type": "Program",
      "start": 0,
      "end": 16,
      "body": [
        {
          "type": "ExpressionStatement",
          "start": 0,
          "end": 16,
          "expression": {
            "type": "MemberExpression",
            "start": 0,
            "end": 16,
            "object": {
              "type": "MemberExpression",
              "start": 0,
              "end": 11,
              "object": {
                "type": "MemberExpression",
                "start": 0,
                "end": 6,
                "object": {
                  "type": "Identifier",
                  "start": 0,
                  "end": 1,
                  "name": "a"
                },
                "property": {
                  "type": "Literal",
                  "start": 2,
                  "end": 5,
                  "value": "b",
                  "raw": "'b'"
                },
                "computed": true
              },
              "property": {
                "type": "Literal",
                "start": 7,
                "end": 10,
                "value": "c",
                "raw": "'c'"
              },
              "computed": true
            },
            "property": {
              "type": "Literal",
              "start": 12,
              "end": 15,
              "value": "d",
              "raw": "'d'"
            },
            "computed": true
          }
        }
      ],
      "sourceType": "module"
    }

这个题从AST角度看就很简单了,a[‘b’][‘c’][‘d’]和a.b.c.d,转换成AST前者的的树是含计算的,后者只是string literal,天然前者会消耗更多的计算成本,时间也更长。如果是按照直接解释运行的话,无非就是a[‘b’]的方式多了一个Literal的转换,实际上是基本没有什么区别

然后在github看到这题目的另一个描述:
[北京的月亮和上海的月亮, 哪个更圆?]

别看这题,题目上每个字都能看懂,但是里面涉及到的知识,暗藏杀鸡 这题要往深处走,会涉及天文学、气象学、地球经纬设计等底层知识

看到这儿,笑出猪笑,觉得上面的描述,也是暗藏杀鸡

上述题目通过AST解构,确实有一丢丢的性能差异。那么AST是啥?

在计算机科学中,ast全称为:抽象语法树(Abstract Syntax Tree,AST),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

很多语言有AST结构编译语言,例如Java、JavaScript、Python。
这边介绍下JavaScript的ast抽象树:
用个例子:

function add(a, b) {
    return a + b
}

首先,我们拿到的这个语法块,是一个FunctionDeclaration(函数定义)对象。

用力拆开,它成了三块:

一个id,就是它的名字,即add
两个params,就是它的参数,即[a, b]
一块body,也就是大括号内的一堆东西
add没办法继续拆下去了,它是一个最基础Identifier(标志)对象,用来作为函数的唯一标志,就像人的姓名一样。

{
    name: 'add'
    type: 'identifier'
    ...
}

params继续拆下去,其实是两个Identifier组成的数组。之后也没办法拆下去了。

[
    {
        name: 'a'
        type: 'identifier'
        ...
    },
    {
        name: 'b'
        type: 'identifier'
        ...
    }
]

接下来,我们继续拆开body
我们发现,body其实是一个BlockStatement(块状域)对象,用来表示是{return a + b}

打开Blockstatement,里面藏着一个ReturnStatement(Return域)对象,用来表示return a + b

继续打开ReturnStatement,里面是一个BinaryExpression(二项式)对象,用来表示a + b

继续打开BinaryExpression,它成了三部分,left,operator,right

operator 即+
left 里面装的,是Identifier对象 a
right 里面装的,是Identifer对象 b
就这样,我们把一个简单的add函数拆解完毕,用图表示就是:
在这里插入图片描述
那么,上面我们提到的Identifier、Blockstatement、ReturnStatement、BinaryExpression, 这一个个小部件的说明书去哪查?
请查看AST对象文档

ES6 转 ES5 目前行业标配是用 Babel,转换的大致流程如下跟AST就有关系

解析:解析代码字符串,生成 AST;
转换:按一定的规则转换、修改 AST;
生成:将修改后的 AST 转换成普通代码

结语:其实本码农并不想了解这些,只是行走在各种博文之间的搬运工。分享给各位读者

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值