Reference

AST 与前端工程化实战
AST抽象语法树——最基础的javascript重点知识,99%的人根本不了解
13 个示例快速入门 JS 抽象语法树

AST Explorer

https://astexplorer.net/

概念

JavaScript解析:
  • 词法分析(Lexical Analysis) :把JavaScript代码(字符串)的字符流(char stream)按照ECMAScript标准转换为记号流(token stream)。
  • 语法分析(Syntactic Analysis):将词法单元流转换成一个由元素逐级嵌套组成的语法结构树,AST

Demo

代码:

var AST = "is Tree";

词法分析 => 记号流

Keyword: var

Identifier: AST

Punctuator: =

String: "is Tree"

Punctuator: ;

语法分析 => AST

AST 工具: Recast

解析器(recast.parse): code => AST
Code Case 1
const recast = require("recast");const code =  `  function add(a, b) {    return a +      // 有什么奇怪的东西混进来了      b  }  `const ast = recast.parse(code);const add  = ast.program.body[0]console.log(add)
Output Of Case 1
{    "type": "FunctionDeclaration",    "id": {        "type": "Identifier",        "name": "add"    },    "params": [        {            "type": "Identifier",            "name": "a"        },        {            "type": "Identifier",            "name": "b"        }    ],    "body": {        "type": "BlockStatement",        "body": [            {                "type": "ReturnStatement",                "argument": {                    "type": "BinaryExpression",                    "operator": "+",                    "left": {                        "type": "Identifier",                        "name": "a"                    },                    "right": {                        "type": "Identifier",                        "name": "b",                        "comments": [                            {                                "type": "Line",                                "value": " 有什么奇怪的东西混进来了",                                "loc": {},                                "leading": true,                                "trailing": false                            }                        ]                    }                }            }        ]    },    "generator": false,    "expression": false,    "async": false}
制作模具 (recast.types.builders): AST => code
Code Case 2

在Code Case 1代码后加入以下代码,对ast进行重新组装:

  • 将add方法改为箭头函数
  • 增加squareSum计算平方和方法
// 引入变量声明,变量符号,函数声明三种“模具”const {     variableDeclaration,     variableDeclarator,     identifier: id,     arrowFunctionExpression,     binaryExpression,    blockStatement,    returnStatement } = recast.types.builders// 将准备好的组件置入模具,并组装回原来的ast对象。// 将add方法改为箭头函数ast.program.body[0] = variableDeclaration("const", [  variableDeclarator(add.id, arrowFunctionExpression(    add.params,    binaryExpression('+', ...add.params)  ))]);// 新增squareSum计算平方和方法ast.program.body.push(variableDeclaration('var', [    variableDeclarator(id('squareSum'), arrowFunctionExpression(        [id('a'), id('b')],        blockStatement([            variableDeclaration('let', [              variableDeclarator(id('c'), binaryExpression('*', id('a'), id('a'))),              variableDeclarator(id('d'), binaryExpression('*', id('b'), id('b')))]),            returnStatement(binaryExpression('+', id('c'), id('d')))        ])            ))]))//将AST对象重新转回可以阅读的代码const output = recast.print(ast).code;console.log(output)
Output Of Case 2
const add = (a, b) => a + b;var squareSum = (a, b) => {  let c = a * a, d = b * b;  return c + d;};

树节点遍历 (recast.types.visit)

recast.visit(ast, {    visitExpressionStatement: function(path) {        const { node} = path;    },    visitBlockStatement(path) {        // do something here    }});

应用场景

  1. 解释器和编译器
  2. 静态代码分析(抽离重复代码,判断代码相似性)
  3. 代码转换

  4. 代码格式化