>[success] # 遍历节点 -- traverser ~~~ 1.经过ast 语法树解析后,以下面这个babel 生成的简化ast 语法树为例 { program: { type: "Program", body: [ { type: "VariableDeclaration", declarations: [ { type: "VariableDeclarator", id: { type: "Identifier", name: "a", }, init: { type: "NumericLiteral", value: 1, }, }, ], kind: "let", }, { type: "ExpressionStatement", expression: { type: "AssignmentExpression", operator: "=", left: { type: "Identifier", name: "a", }, right: { type: "NumericLiteral", value: 2, }, }, }, { type: "ExpressionStatement", expression: { type: "BinaryExpression", left: { type: "Identifier", name: "a", }, operator: "===", right: { type: "NumericLiteral", value: 1, }, }, }, ], }, }; ~~~ ![](https://img.kancloud.cn/0f/ab/0fab692bb7efcefc96faa19e4b5adb44_789x578.png) >[info] ## 遍历节点 ~~~ 1.现在想遍历节点,但是在遍历时候想拿到特定节点值,但是可以发现babel ast不像我们之前开 发过程中数据结构每个子节点都是固定属性,相对来说不同节点的对应包含自己的数据类型是 不同的。下面案例来说明 ~~~ >[danger] ##### 案例一 ~~~ 1.要设计一个可以获取指定节点数据,并且可以获取其父节点数据,其实整体设计思路就是递归 时候判断是否是需要的类型,如果需要通过回调函数获取 2.下面数据先将案例简化。默认所有子节点都是'body' ~~~ ~~~ const aa = { type: "Program", body: [ { type: "CallExpression", name: "add", body: [ { type: "NumberLiteral", value: "2" }, { type: "CallExpression", name: "subtract", body: [ { type: "NumberLiteral", value: "4" }, { type: "NumberLiteral", value: "2" }, ], }, ], }, ], }; function traverser(ast, cfg) { if (ast.body && ast.body.length > 0) { // 递归父节点中子节点 ast.body.forEach((item) => { // 判断类型使用回调 let fun = cfg[item.type]; if (fun) { // 获取父节点 fun(ast, item.name); } traverser(item, cfg); }); } } traverser(aa, { CallExpression(ast, name) { console.log(name, ast); }, }); ~~~ >[danger] ##### 针对更复杂的案例 ~~~ 1.下面案例针对类型做了更具体的针对父节点对应子节点进行了展开 ~~~ ~~~ const a = { type: "Program", body: [ { type: "CallExpression", name: "add", params: [ { type: "NumberLiteral", value: "2" }, { type: "CallExpression", name: "subtract", params: [ { type: "NumberLiteral", value: "4" }, { type: "NumberLiteral", value: "2" }, ], }, ], }, ], }; // 遍历器 参数:ast 和 visitor function traverser(ast, visitor) { // 定义方法 traverseArray // 用于遍历 AST节点数组,对数组中每个元素调用 traverseNode 方法。 function traverseArray(array, parent) { array.forEach((child) => { traverseNode(child, parent); }); } // 定义方法 traverseNode // 用于处理每个 AST 节点,接受一个 node 和它的父节点 parent 作为参数 function traverseNode(node, parent) { // 获取 visitor 上对应方法的对象 let methods = visitor[node.type]; if (methods) { methods(node, parent); } switch (node.type) { // 根节点 case "Program": traverseArray(node.body, node); break; // 函数调用 case "CallExpression": traverseArray(node.params, node); break; // 数值和字符串,忽略 case "NumberLiteral": case "StringLiteral": break; // 当遇到无法识别的字符,抛出错误提示,并退出 default: throw new TypeError(node.type); } if (methods && methods.exit) { methods.exit(node, parent); } } // 首次执行,开始遍历 traverseNode(ast, null); } traverser(a, { CallExpression(ast, name) { console.log(name, ast); }, }); ~~~