抽象语法书的用途
抽象语法树定义
通过JavaScript Parser把代码转化为一颗抽象语法书(AST),这棵树定义了代码的结构,,通过操纵这棵树,我们可以精准的定位到声明语句,赋值语句,运算语句,实现对代码的分析,优化,变更等操作
- 在计算机科学中,抽象语法树(abstract syntax tree),或者语法树(syntax tree)是源代码抽象语法结构的树状表现形式,这里特质编程语言的源代码。
JavaScript的语法是为了给开发者更好的编程而设计的,但是不适合程序的理解.所以需要转化为AST来更适合程序的分析,浏览器编译一般会把源码转化为AST来进行进一步的分析等其他操作- js代码
function fn () {}
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "fn",
"range": [
9,
11
]
},
"params": [
{
"type": "Identifier",
"name": "a",
"range": [
13,
14
]
}
],
"body": {
"type": "BlockStatement",
"body": [],
"range": [
15,
17
]
},
"generator": false,
"expression": false,
"async": false,
"range": [
0,
17
]
}
],
"sourceType": "module",
"range": [
0,
17
]
}
JavaScript Parser
- 可以吧源码转化为抽象语法书的解析器
- 浏览器会把js源码通过解析器转化为抽象语法书,再进一步转化为字节码或直接生成机器码
- 一般来说每个js引擎都有自己的抽象语法树格式,chrome的v8引擎,Firefox的SpiderMonkey引擎等等,MDN提供了详细的SpiderMonkey SAT format的详细说明.
- 常用的JavaScript Parser有:
scprima
可以将一个源代码语法转换为AST
npm i esprima
const esprima = require('esprima')
let code = 'function ast () {}'
let ast = esprima.parse(code)
console.log(ast)
Script {
type: 'Program',
body:
[ FunctionDeclaration {
type: 'FunctionDeclaration',
id: [Identifier],
params: [],
body: [BlockStatement],
generator: false,
expression: false,
async: false } ],
sourceType: 'script' }
estraverse
遍历语法书进行更新AST
const esprima = require('esprima')
const estraverse = require('estraverse')
let code = 'function ast () {}'
let ast = esprima.parse(code)
estraverse.traverse(ast, {
enter (node, parent) {
if (node.type === 'Identifier') {
node.name += ' enter'
}
console.log('enter', node.type)
},
leave (node, parent) {
if (node.type === 'Identifier') {
node.name += ' leave'
}
console.log('leave', node.type)
}
})
enter Identifier
leave Identifier
enter BlockStatement
leave BlockStatement
leave FunctionDeclaration
leave Program
escodegen
将AST重新生成源码
const esprima = require('esprima')
const estraverse = require('estraverse')
const escodegen = require('escodegen')
let code = 'function ast () {}'
let ast = esprima.parse(code)
estraverse.traverse(ast, {
enter (node, parent) {
if (node.type === 'Identifier') {
node.name += '_enter'
}
},
leave (node, parent) {
if (node.type === 'Identifier') {
node.name += '_leave'
}
}
})
let result = escodegen.generate(ast)
console.log(result)
function ast_enter_leave() {
}
转换箭头函数
访问者模式Visitor对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行的操做也不同步
-
-
转换代码
-
visitor有两种写法
let visitor = {
ArrowFunctionExpression: {
enter (node, parent) {
console.log('enter', node.type)
},
leave (node, parent) {
console.log('leave', node.type)
}
}
}
let visitor = {
ArrowFunctionExpression (path) {
let params = path.node.params
let body = types.blockStatement([
types.returnStatement(path.node.body)
])
let fn = types.functionExpression(null, params, body, false, false)
path.replaceWith(fn)
}
}
-
匹配规则,,就是变量名等于我们插件的函数名
const babel = require('babel-core')
const types = require('babel-types')
let code = `let sum = (a, b) => a + b;`
let visitor = {
ArrowFunctionExpression (path) {
let params = path.node.params
let body = types.blockStatement([
types.returnStatement(path.node.body)
])
let fn = types.functionExpression(null, params, body, false, false)
path.replaceWith(fn)
}
}
let arrowPlugin = { visitor }
let result = babel.transform(code, {
plugins: [
arrowPlugin
]
})
console.log('result', result.code)