什么是抽象树?

在计算机科学中,抽象语法树(abstract syntax tree 或者缩写为 AST),或者语法树(syntax tree)

源代码抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。

之所以说语法是「抽象」的,是因为这里的语法并不会表示出真实语法中出现的每个细节

我们常用的浏览器就是通过将 js 代码转化为抽象语法树来进行下一步的分析等其他操作。所以将 js 转化为抽象语法树更利于程序的分析

  • var 是一个关键字
  • AST 是一个定义者
  • = 是 Equal 等号的叫法有很多形式,在后面我们还会看到
  • is tree 是一个字符串
  • ; 就是 Semicoion

首先一段代码转换成的抽象语法树是一个对象,该对象会有一个顶级的 type 属性 Program;第二个属性是 body 是一个数组。

body 数组中存放的每一项都是一个对象,里面包含了所有的对于该语句的描述信息

type:         描述该语句的类型  --> 变量声明的语句
kind:         变量声明的关键字  --> var
declaration:  声明内容的数组,里面每一项也是一个对象
            type: 描述该语句的类型
            id:   描述变量名称的对象
                type: 定义
                name: 变量的名字
            init: 初始化变量值的对象
                type:   类型
                value:  值 "is tree" 不带引号
                row:    ""is tree"" 带引号

词法分析和语法分析

JavaScript 是解释型语言,一般通过 词法分析 -> 语法分析 -> 语法树,就可以开始解释执行了

词法分析:也叫扫描,是将字符流转换为记号流(tokens),它会读取我们的代码然后按照一定的规则合成一个个的标识

比如说:var a = 2 ,这段代码通常会被分解成 var、a、=、2

;[
  { type: 'Keyword', value: 'var' },
  { type: 'Identifier', value: 'a' },
  { type: 'Punctuator', value: '=' },
  { type: 'Numeric', value: '2' },
]

使用场景

  • 语法检查、代码风格检查、格式化代码、语法高亮、错误提示、自动补全等
  • 代码混淆压缩
  • 优化变更代码,改变代码结构等

在线astexplorer


选择 babel 库,使用最多的 ast 处理库

文档:https://babeljs.io/docs/en/

我们只用 https://www.babeljs.cn/docs/babel-parser 部分


AST 基础工具

安装

cnpm install @babel/parser
cnpm install @babel/traverse
cnpm install @babel/types
cnpm install @babel/generator

parse: 将 js 代码转化为 AST

generator: 将 AST 转化会 js 代码

traverse: 节点遍历操作的方便功能

types: 提供一个与格式相关的对象合集,可以操作从而生成节点


1、abel/parser

在babel中编译器插件是@babel/parser,其作用就是将源码转换为AST,使用前需要npm install @babel/parser,使用方法如下:

const {parse} = require("@babel/parser");  //require返回一个object,使用解构赋值,可以只取出对应名字的属性
const js_code = "const res = 'Rannie';";
const ast_code = parse(js_code);
console.log(ast_code.program.body[0].declarations[0].id.name);
console.log(ast_code.program.body[0].declarations[0].init.extra.rawValue);

2、abel/generator

其作用就是将转换好的 ast 重新生成代码。这样的代码就就可以安全的在浏览器运行。

const {parse} = require("@babel/parser");
const generate = require('@babel/generator').default
const js_code = `const res = 'Rannie';`;
const ast_code = parse(js_code);
const js_code2 = generate(ast_code).code
console.log(js_code2)

3、babel/traverse

这个插件的作用是对ast进行遍历parse,在迭代的过程中可以定义回调函数,回调函数的参数提供了丰富的增、删、改、查以及类型断言的方法,比如replaceWith/remove/find/isMemberExpression


const {parse} = require("@babel/parser");  //require返回一个object,使用解构赋值,可以只取出对应名字的属性
const traverse = require('@babel/traverse').default
const generate = require('@babel/generator').default

var js_code = `
    a = 1;
    a = 1;
    a = 1;
`
const ast_code = parse(js_code)

const visitor = {
    Literal(path){
        path.node.value = 100;
        console.log(path.node.value);
    },
    Identifier(path){
        path.node.name = 'b'
    }
}

traverse(ast_code, visitor);
const result_js_code = generate(ast_code).code
console.log(result_js_code)
const {parse} = require("@babel/parser");  //require返回一个object,使用解构赋值,可以只取出对应名字的属性
const traverse = require('@babel/traverse').default
const generate = require('@babel/generator').default

js_code = `function hello(ofN1){    console["\x6c\x6f\x67"](ofN1)};`
const ast_code = parse(js_code)   // Literal
const visitor = {
    StringLiteral(path){
        path.node.extra.raw = '\'' + path.node.extra.rawValue + '\''
    },
}
traverse(ast_code, visitor);
const result_js_code = generate(ast_code).code
console.log(result_js_code)

 posted on 2021-04-16 18:32  Rannie`  阅读(115)  评论(0编辑  收藏  举报
去除动画
找回动画