广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >关于前端要知道的 AST知识
  • 585
分享到

关于前端要知道的 AST知识

前端前端AST知识 2023-05-15 17:05:39 585人浏览 八月长安
摘要

目录认识 ASTestree[1]认识 acorn[2]基本操作实现普通函数转换为箭头函数认识 AST 定义:在计算机科学中,抽象语法树是源代码语法结构的一种抽象表示。它以

认识 AST

定义:在计算机科学中,抽象语法树是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。

从定义中我们只需要知道一件事就行,那就是 AST 是一种树形结构,并且是某种代码的一种抽象表示。

在线可视化网站:https://astexplorer.net/ ,利用这个网站我们可以很清晰的看到各种语言的 AST 结构。

estree[1]

estree 就是 es 语法对应的标准 AST,作为一个前端也比较方便理解。我们以官方文档为例

Https://GitHub.com/estree/estree/blob/master/es5.md

下面看一个代码

console.log('1')

AST 为

{
  "type": "Program",
  "start": 0, // 起始位置
  "end": 16, // 结束位置,字符长度
  "body": [
    {
      "type": "ExpressionStatement", // 表达式语句
      "start": 0,
      "end": 16,
      "expression": {
        "type": "CallExpression", // 函数方法调用式
        "start": 0,
        "end": 16,
        "callee": {
          "type": "MemberExpression", // 成员表达式 console.log
          "start": 0,
          "end": 11,
          "object": {
            "type": "Identifier", // 标识符,可以是表达式或者结构模式
            "start": 0,
            "end": 7,
            "name": "console"
          },
          "property": {
            "type": "Identifier", 
            "start": 8,
            "end": 11,
            "name": "log"
          },
          "computed": false, // 成员表达式的计算结果,如果为 true 则是 console[log], false 则为 console.log
          "optional": false
        },
        "arguments": [ // 参数
          {
            "type": "Literal", // 文字标记,可以是表达式
            "start": 12,
            "end": 15,
            "value": "1",
            "raw": "'1'"
          }
        ],
        "optional": false
      }
    }
  ],
  "sourceType": "module"
}

看两个稍微复杂的代码

const b = { a: 1 };
const { a } = b;
function add(a, b) {
    return a + b;
}

这里建议读者自己将上述代码复制进上面提到的网站中,自行理解 estree 的各种节点类型。当然了,我们也不可能看一篇文章就记住那么多类型,只要心里有个大致的概念即可。

认识 acorn[2]

javascript 编写的 JavaScript 解析器,类似的解析器还有很多,比如 Esprima[3] 和 Shift[4] ,关于他们的性能,Esprima 的官网给了个测试地址[5],但是由于 acron 代码比较精简,且 webpack 和 eslint 都依赖 acorn,因此我们这次从 acorn 下手,了解如何使用 AST。

基本操作

acorn 的操作很简单

import * as acorn from 'acorn';
const code = 'xxx';
const ast = acorn.parse(code, options)

这样我们就能拿到代码的 ast 了,options 的定义如下

  interface Options {
    ecmaVersion: 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 'latest'
    sourceType?: 'script' | 'module'
    onInsertedSemicolon?: (lastTokEnd: number, lastTokEndLoc?: Position) => void
    onTrailinGComma?: (lastTokEnd: number, lastTokEndLoc?: Position) => void
    allowReserved?: boolean | 'never'
    allowReturnOutsideFunction?: boolean
    allowImportExportEverywhere?: boolean
    allowAwaitOutsideFunction?: boolean
    allowSuperOutsideMethod?: boolean
    allowHashBang?: boolean
    locations?: boolean
    onToken?: ((token: Token) => any) | Token[]
    onComment?: ((
      isBlock: boolean, text: string, start: number, end: number, startLoc?: Position,
      endLoc?: Position
    ) => void) | Comment[]
    ranges?: boolean
    program?: node
    sourceFile?: string
    directSourceFile?: string
    preserveParens?: boolean
  }
  • ecmaVersion ECMA 版本,默认时 es7
  • locations 默认为 false,设置为 true 时节点会携带一个 loc 对象来表示当前开始与结束的行数。
  • onComment 回调函数,每当代码执行到注释的时候都会触发,可以获取当前的注释内容

获得 ast 之后我们想还原之前的函数怎么办,这里可以使用 astring[6]

import * as astring from 'astring';
 
const code = astring.generate(ast);

实现普通函数转换为箭头函数

接下来我们就可以利用 AST 来实现一些字符串匹配不太容易实现的操作,比如将普通函数转化为箭头函数。

我们先来看两个函数的AST有什么区别

function add(a, b) {
    return a + b;
}
const add = (a, b) => {
    return a + b;
}
{
  "type": "Program",
  "start": 0,
  "end": 41,
  "body": [
    {
      "type": "FunctionDeclaration",
      "start": 0,
      "end": 40,
      "id": {
        "type": "Identifier",
        "start": 9,
        "end": 12,
        "name": "add"
      },
      "expression": false,
      "generator": false,
      "async": false,
      "params": [
        {
          "type": "Identifier",
          "start": 13,
          "end": 14,
          "name": "a"
        },
        {
          "type": "Identifier",
          "start": 16,
          "end": 17,
          "name": "b"
        }
      ],
      "body": {
        "type": "BlockStatement",
        "start": 19,
        "end": 40,
        "body": [
          {
            "type": "ReturnStatement",
            "start": 25,
            "end": 38,
            "argument": {
              "type": "BinaryExpression",
              "start": 32,
              "end": 37,
              "left": {
                "type": "Identifier",
                "start": 32,
                "end": 33,
                "name": "a"
              },
              "operator": "+",
              "right": {
                "type": "Identifier",
                "start": 36,
                "end": 37,
                "name": "b"
              }
            }
          }
        ]
      }
    }
  ],
  "sourceType": "module"
}
{
  "type": "Program",
  "start": 0,
  "end": 43,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 43,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 6,
          "end": 43,
          "id": {
            "type": "Identifier",
            "start": 6,
            "end": 9,
            "name": "add"
          },
          "init": {
            "type": "ArrowFunctionExpression",
            "start": 12,
            "end": 43,
            "id": null,
            "expression": false,
            "generator": false,
            "async": false,
            "params": [
              {
                "type": "Identifier",
                "start": 13,
                "end": 14,
                "name": "a"
              },
              {
                "type": "Identifier",
                "start": 16,
                "end": 17,
                "name": "b"
              }
            ],
            "body": {
              "type": "BlockStatement",
              "start": 22,
              "end": 43,
              "body": [
                {
                  "type": "ReturnStatement",
                  "start": 28,
                  "end": 41,
                  "argument": {
                    "type": "BinaryExpression",
                    "start": 35,
                    "end": 40,
                    "left": {
                      "type": "Identifier",
                      "start": 35,
                      "end": 36,
                      "name": "a"
                    },
                    "operator": "+",
                    "right": {
                      "type": "Identifier",
                      "start": 39,
                      "end": 40,
                      "name": "b"
                    }
                  }
                }
              ]
            }
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}

找到区别之后我们就可以有大致的思路

  1. 找到 FunctionDeclaration
  2. 将其替换为VariableDeclaration VariableDeclarator 节点
  3. 在 VariableDeclarator 节点的 init 属性下新建 ArrowFunctionExpression 节点
  4. 并将 FunctionDeclaration 节点的相关属性替换到 ArrowFunctionExpression 上即可

但是由于 acorn 处理的 ast 只是单纯的对象,并不具备类似 dom 节点之类的对节点的操作能力,如果需要操作节点,需要写很多工具函数, 所以我这里就简单写一下。

import * as acorn from "acorn";
import * as astring from 'astring';
import { createNode, walkNode } from "./utils.js";
 
const code = 'function add(a, b) { return a+b; } function dd(a) { return a + 1 }';
console.log('in:', code);
const ast = acorn.parse(code);
 
walkNode(ast, (node) => {
    if(node.type === 'FunctionDeclaration') {
        node.type = 'VariableDeclaration';
        const variableDeclaratorNode = createNode('VariableDeclarator');
        variableDeclaratorNode.id = node.id;
        delete node.id;
        const arrowFunctionExpressionNode = createNode('ArrowFunctionExpression');
        arrowFunctionExpressionNode.params = node.params;
        delete node.params;
        arrowFunctionExpressionNode.body = node.body;
        delete node.body;
        variableDeclaratorNode.init = arrowFunctionExpressionNode;
        node.declarations = [variableDeclaratorNode];
        node.kind = 'const';
    }
})
 
console.log('out:', astring.generate(ast))

结果如下

如果想要代码更加健壮,可以使用 recast[7],提供了对 ast 的各种操作

// 用螺丝刀解析机器
const ast = recast.parse(code);
 
// ast可以处理很巨大的代码文件
// 但我们现在只需要代码块的第一个body,即add函数
const add  = ast.program.body[0]
 
console.log(add)
 
// 引入变量声明,变量符号,函数声明三种“模具”
const {variableDeclaration, variableDeclarator, functionExpression} = recast.types.builders
 
// 将准备好的组件置入模具,并组装回原来的ast对象。
ast.program.body[0] = variableDeclaration("const", [
  variableDeclarator(add.id, functionExpression(
    null, // Anonymize the function expression.
    add.params,
    add.body
  ))
]);
 
//将AST对象重新转回可以阅读的代码
const output = recast.print(ast).code;
 
console.log(output)

这里只是示例代码,展示 recast 的一些操作,最好的情况还是能遍历节点自动替换。

这样我们就完成了将普通函数转换成箭头函数的操作,但 ast 的作用不止于此,作为一个前端在工作中可能涉及 ast 的地方,就是自定义 eslint 、 stylelint 等插件

到此这篇关于关于前端要知道的 AST知识的文章就介绍到这了,更多相关前端AST知识内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 关于前端要知道的 AST知识

本文链接: https://www.lsjlt.com/news/209268.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 关于前端要知道的 AST知识
    目录认识 ASTestree[1]认识 acorn[2]基本操作实现普通函数转换为箭头函数认识 AST 定义:在计算机科学中,抽象语法树是源代码语法结构的一种抽象表示。它以...
    99+
    2023-05-15
    前端 前端AST知识
  • 前端要知道的AST知识有哪些
    本篇内容主要讲解“前端要知道的AST知识有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“前端要知道的AST知识有哪些”吧!认识 AST定义:在计算机科学中,抽象语法树是源代码语法结构的一种抽...
    99+
    2023-07-06
  • 分享Web前端的相关知识
    这篇文章主要介绍“分享Web前端的相关知识”,在日常操作中,相信很多人在分享Web前端的相关知识问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分享Web前端的相关知识”的疑惑...
    99+
    2022-10-19
  • PHP 容器:你需要知道的一切关于数据类型管理的知识
    PHP 是一种广泛使用的开源服务器端脚本语言,用于 Web 开发。在 PHP 中,容器是一种重要的数据结构,它可以存储和管理多个数据类型。本文将介绍 PHP 容器的概念、类型和使用方法。 一、PHP 容器的概念 PHP 容器是一种数据结构...
    99+
    2023-09-20
    学习笔记 容器 数据类型
  • 关于Android多渠道打包的进阶知识
    多渠道打包进阶知识 文章开始前,先看一下下面这种情况: android { productFlavors { //100 个多渠道配置 } /...
    99+
    2022-11-12
  • 关于mysql自增id,你需要知道的
    导读:在使用MySQL建表时,我们通常会创建一个自增字段(AUTO_INCREMENT),并以此字段作为主键。本篇文章将以问答的形式讲述关于自增id的一切。 注: 本文所讲的都是基于Innodb存储引擎。 1.M...
    99+
    2022-05-21
    mysql 自增id mysql 自增 mysql id
  • 关于msyql事务隔离你要知道
    什么是事务? 事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。数据库事务通常包含了一个序列的对数据库的读/写操作。包含有以下两个目的: 为数据库操作序列提供了一个从失败中恢复到正...
    99+
    2022-05-11
    mysql 事务隔离 mysql 事务
  • 有关前端基础知识整理汇总
    这篇文章主要介绍“有关前端基础知识整理汇总”,在日常操作中,相信很多人在有关前端基础知识整理汇总问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”有关前端基础知识整理汇总”的疑惑...
    99+
    2022-10-19
  • C#中所要知道的知识点有哪些
    本文小编为大家详细介绍“C#中所要知道的知识点有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#中所要知道的知识点有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.在C#中,类名首字母需大写。如:c...
    99+
    2023-07-05
  • Python新手需要知道的5条知识点
      1.什么是PEP8  PEP8是一个编程规范,内容是一些关于如何让你的程序更具可读性的建议。  其主要内容包括代码编排、文档编排、空格的使用、注释、文档描述、命名规范、编码建议等。  2.什么是pickling和unpickling  ...
    99+
    2023-06-02
  • 你需要理解的关于MySQL的锁知识
    一、前言 MySQL 的锁按照范围可以分为全局锁、表锁、行锁,其中行锁是由数据库引擎实现的,并不是所有的引擎都提供行锁,MyISAM 就不支持行锁,所以文章介绍行锁会以InnoDB引擎为例来介绍行锁。 二、...
    99+
    2022-10-18
  • 前端CSS必须要学的知识点有哪些
    本文小编为大家详细介绍“前端CSS必须要学的知识点有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“前端CSS必须要学的知识点有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识...
    99+
    2022-10-19
  • 学习数据库,你不得不知道的关于索引的小知识
      有了图,接下来,就是对于我今天看的内容觉得比较好的分享,文末有福利 1、select * 对效率的影响 在我们平时的代码编写或面试题中,很多人都会疑惑:select * 到底合理吗? 如果说不合理...
    99+
    2018-01-19
    学习数据库,你不得不知道的关于索引的小知识
  • Javascript基础知识中关于内置对象的知识
    目录1、内置对象介绍1.1Math对象1.2Math中的方法1.3Date对象2、Date中的方法3、经典案例:倒计时效果:4、Array数组对象4.1数组的创建4.2数组中的常用方...
    99+
    2022-11-12
  • ASP新手要知道的基础知识有哪些
    ASP新手要知道的基础知识有哪些,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。我们都知道,ASP是Active Server Page的缩写,意为“动态服务器页...
    99+
    2023-06-17
  • web前端开发都需要学习哪些知识
    web前端开发在目前的IT行业是一个比较重要的岗位,web前端开发是直接与用户接触,前端体验的好坏,以及美观程度是非常重要的,毕竟现在是一个看脸的时代。前端开发需要学习的知识非常的多,非常的杂,一个前端页面的开发,需要涉及很多的知识,且前端...
    99+
    2023-06-03
  • web前端面试需要掌握哪些知识点
    本篇内容主要讲解“web前端面试需要掌握哪些知识点”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“web前端面试需要掌握哪些知识点”吧!  基本功考察  关于Html  1. html语义化标签的...
    99+
    2023-06-04
  • web前端是做什么的知识
    随着互联网的发展,Web前端开发成为一个越来越重要的职业。Web前端开发是指通过编写HTML、CSS、JavaScript等代码,实现页面的设计和交互效果,最终呈现出给用户的网页界面。从某种意义上来说,Web前端开发就是将网页设计师所绘制出...
    99+
    2023-05-20
  • 前端大数的运算及相关知识有哪些
    本篇内容主要讲解“前端大数的运算及相关知识有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“前端大数的运算及相关知识有哪些”吧! 背景前段时间我在公...
    99+
    2022-10-19
  • Bash文件编程算法:Java开发者需要知道的关键知识点?
    Bash文件编程是一种非常强大的工具,它可以帮助开发者自动化执行一些重复性的任务。对于Java开发者来说,掌握Bash文件编程算法是非常有必要的,因为这可以帮助他们更高效地管理和部署Java应用程序。在本篇文章中,我们将介绍一些Bash文...
    99+
    2023-06-25
    bash 文件 编程算法
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作