说起 AST 语法,很多前端小伙伴都是不陌生,但是真正使用的却少之又少,本文简单总结一下自己学习和使用 ast 的心路历程。
首先什么是 AST 呢?
JavaScript 是一种解释类型语言,在执行前要经历词法分析、语法分析(AST)、代码生成 三个阶段
-
词法分析:JavaScript将代码串进行分析为词法单元
例如代码块 var answer = 6; 会被分解成为 var、answer、=、6、; [ { "type": "Keyword", "value": "var" }, { "type": "Identifier", "value": "answer" }, { "type": "Punctuator", "value": "=" }, { "type": "Numeric", "value": "6" }, { "type": "Punctuator", "value": ";" } ] 复制代码
具体的
type
值可以参考@babel/types
官方介绍 @babel/types -
语法分析:
将词法单元转为由元素嵌套组成的语法结构树,就是我们所说的抽象语法树(abstract syntax code,AST)
{ "type": "File", "start": 0, "end": 16, "loc": { "start": { "line": 1, "column": 0 }, "end": { "line": 2, "column": 0 } }, "errors": [], "program": { "type": "Program", "start": 0, "end": 16, "loc": { "start": { "line": 1, "column": 0 }, "end": { "line": 2, "column": 0 } }, "sourceType": "module", "interpreter": null, "body": [ { "type": "VariableDeclaration", "start": 0, "end": 15, "loc": { "start": { "line": 1, "column": 0 }, "end": { "line": 1, "column": 15 } }, "declarations": [ { "type": "VariableDeclarator", "start": 4, "end": 14, "loc": { "start": { "line": 1, "column": 4 }, "end": { "line": 1, "column": 14 } }, "id": { "type": "Identifier", "start": 4, "end": 10, "loc": { "start": { "line": 1, "column": 4 }, "end": { "line": 1, "column": 10 }, "identifierName": "answer" }, "name": "answer" }, "init": { "type": "NumericLiteral", "start": 13, "end": 14, "loc": { "start": { "line": 1, "column": 13 }, "end": { "line": 1, "column": 14 } }, "extra": { "rawValue": 6, "raw": "6" }, "value": 6 } } ], "kind": "var" } ], "directives": [] }, "comments": [] } 复制代码
其实我们单看
body
就可以,它里面包含了我们的语法树结构,其实里面的一些字段start
、end
、loc
这三个值对我们来说基本上是没有大作用的我们可以在这一步中对源代码进行添加、更新和删除节点,
- 使用 @babel/parser 将字符串转为 AST 语法树,
- 使用 @babel/traverse 转换代码
const parser = require('@babel/parser'); const traverse = require('@babel/traverse'); var answer = 6; const ast = parser.parse(code); traverse.default(ast, { enter(path) { if(path.isIdentifier({name: "answer"})) { path.node.name = 'question'; } if(path.isLiteral({value: 6})) { path.node.value = 666; } }, }); const newCode = generator.default(ast, {}, code).code; console.log('newCode: ', newCode) // newCode: var question = 666; 复制代码
我们也可以在语法树中使用 node type 进行代码的修改
traverse.default(ast, { VariableDeclarator(path) { if(path.node.id.name ==='answer'){ path.node.init.value ='666' } } }); 复制代码
-
代码生成:
const generator = require('@babel/generator');
const newCode = generator.default(ast, {}, code).code;
console.log('newCode: ', newCode) // newCode: var question = 666;
复制代码
在前端的开发过程中,不免的需要去开发一些代码工具,我们通过工具可以去修改我们的源代码,从而提升开发效率。在这个情况下,我们就需要分析源代码、修改源代码、生产新的代码。这就会用到 AST
的语法分析
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END