AST学习笔记

window = this和global的区别
1.this-->module.exports
2.window.x=xxx用this输出会报错
一般补环境
window = global
delete global

ast抽象语法树
astexplorer.net
一棵树从file根节点开始,program代表所有代码,body下面expr...代表代码信息

babel是node.js的ast第三方库.。
打开官方文档将所有的工具进行安装。
安装 npm install @babel/types
parser.parse将js代码转化成语法树
generator将语法树转化成js代码
types用来判断节点类型和生成新节点
https://blog.csdn.net/qq_26158277/article/details/120243558

蓝色的key叫节点在node.js中以node存在
variableDeclaration:变量声明节点
FunctionExpression:函数声明节点
BlockStatement:块语句(函数里面的块)
identifier:标识符(变量)
BreakStatement:中断语句break
ContinueStatement:跳过语句continue
update:更新表达式i++
test:二进制表达式,运算判断之类
ForStatment:for块for循环
IfStatment:判断
callExpression:函数调用
MeberExpression:对象的成员
new:NewExpression:new表达式
常用四种工具
generator:ast转js
parse:js转ast
traverse:节点遍历操纵
types:提供对象集合,操作生成节点
throw出错
//最基础
//引入库
const parser = require('@babel/parser');
// const generator = require('@babel/generator',{
//     retainLines:true,//保留行号
//     comment:false,//保留注释
//     compact:false,//正常压缩
//     minified:true,//高压
//     concise:false,//低压
//     jsescOption:{}
// }).default;
//遍历
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;

// //js代码转化为ast(sourceType:'module'不加这个在代码中有impoer时可能会报错)
// const aaa = parser.parse('var a = 1;var b = 2;var c=3;',{sourceType:'module'});
// //替换
// const ast_code = aaa['program']['body'][1]['declarations'][0]['id']['name']='我是最牛逼';
// console.log(ast_code);
// //ast转化为js
// console.log(generator(aaa).code);
// //throw 'end';报错


//遍历和替换
var js_code = 'a=1;a=2;a=3;a=4;';
const bl_ast = parser.parse(js_code);
// console.log(bl_ast);
//visitor自定义的方法
const visitor = {
// ExpressionStatement(path){
//     console.log(path.node.expression.right.value);
// },
    //遍历修改value值
     NumericLiteral(path){
         path.node.value = 100;
      console.log(path.node.value);
  },
    //遍历修改key值
    Identifier(path){
        path.node.name = 'b'
    },
};
traverse(bl_ast,visitor);
const result = generator(bl_ast).code;
console.log(result);






const fs = require('fs');
const {parse} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
var js_code = fs.readFileSync('hx.js',{encoding:'utf-8'});
const generator = require('@babel/generator').default;
const types = require('@babel/types');

const ast_code = parse(js_code);
const visitor = {
    //深度优先,遍历两种节点
    'ExpressionStatement|AssignmentExpression':{
        enter(path){
            console.log(path.toString());
            console.log('1');
        },
        exit(path){
            console.log(path.toString());
            console.log('2')
        },
    }

        //先遍历大节点再遍历小节点
        //ArrayExpression:{
            enter(path){
                console.log(path.toString())
                console.log(generator(path.node).code)
                console.log(path+'');
                //path方法
                path.stop();
                //自动识别替换path方法
                path.replaceWith(types.valueToNode('1234445643333'))
                path.replaceWithSourceString('12344456fdsafa43333')
                //前插入
                path.insertBefore(types.valueToNode('fdskhaksjfhksaka'))
                 //后插入
                path.insertAfter(types.valueToNode('kjjjjjjj'));
                path.stop()
                ///输出node对象
                console.log(path.parent);
               // 输出path对象
                console.log(path.parentPath)
                //打印父节点
                console.log(path.parent.type)
               // path.findParent向上遍历父节点,直到找到,满足回调函数要求为止
                result = path.findParent(function(result){return result.isExpressionStatement()});
                console.log(result.type);
                //容器 key是当前节点在容器中的索引 listkey是容器的名字 只有节点是列表容易才有意义,否则会返回父节点
                console.log(path.container);
                 console.log(path.key);
                 console.log(path.listkey)
                //判断是否是列表
                 console.log(path.inList());
                //获得同级几点的第几个key的path,获得下一个同级节点path.getsibling(path.key+1)
                console.log(path.getsibling(0)+'');
                //获得当前作用域的引用值
                path.evaluate()
            }},
            Identifier(path){
             //获得当前作用域的引用值

                // console.log(path);
                if (path.node.name === 'a'){
                    console.log( path.evaluate())
                };

            },
            // NumericLiteral(path){
                //path删除节点(删除要保证ast节点的完整和流畅,要不然很容易报错)
                // path.remove();
                //前插入

            // },

};
// const visitor = {
//     ArrayExpression:{
//         enter(path){
//             console.log('1');
//         },
//         exit(path){
//             console.log('2')
//         },
//     }
// };
traverse(ast_code,visitor);
const result_js_code = generator(ast_code).code;
console.log(result_js_code);

//
//自动生成节点
// console.log(types.valueToNode('1234445643333'));
// throw '';
//babel库及文件模块导入
const fs = require('fs');

//babel库相关,解析,转换,构建,生产
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const types = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = fs.readFileSync('./hx.js', {encoding: "utf-8"});
let ast = parser.parse(jscode);

// let obj = {
//     name: 'demo',
//     add: function (a, b) {
//         return a + b + 1000;
//     },
//     mul: function (a, b) {
//         return a * b + 1000;
//     },
// };



// types组件练习。判断节点类型和生成新的节点
// &&表示and
// path.xxx取的是key里面的值,其他的都是value中的值
traverse(ast,{
   enter(path){
       //修改name的值
       if (types.isIdentifier(path.node)&& path.node.name === 'a'){
           path.node.name = '我是sm';
       }
       //代码第二种形式
       if(types.isIdentifier(path.node,{name:'a'})){
           path.node.name = '我是sp'
       };
       //断言形式(报错)
       // types.assertIdentifier(path.node)


   }
});
aa = generator(ast).code;
console.log(aa);
// 生成新的节点
let a = t.identifier('我是sp');
let b = t.identifier('b');
let binExpr2 = t.binaryExpression("+", a, b);
let binExpr3 = t.binaryExpression("*", a, b);
let retSta2 = t.returnStatement(t.binaryExpression("+", binExpr2, t.numericLiteral(1000)));
let retSta3 = t.returnStatement(t.binaryExpression("+", binExpr3, t.numericLiteral(1000)));
let bloSta2 = t.blockStatement([retSta2]);
let bloSta3 = t.blockStatement([retSta3]);
let funcExpr2 = t.functionExpression(null, [a, b], bloSta2);
let funcExpr3 = t.functionExpression(null, [a, b], bloSta3);
let objProp1 = t.objectProperty(t.identifier('name'), t.stringLiteral('test'));
let objProp2 = t.objectProperty(t.identifier('add'), funcExpr2);
let objProp3 = t.objectProperty(t.identifier('mul'), funcExpr3);
let objExpr = t.objectExpression([objProp1, objProp2, objProp3]);
let varDec = t.variableDeclarator(t.identifier('obj'), objExpr);
let loaclAst = t.variableDeclaration('let', [varDec]);
let code = generator(loaclAst).code;
console.log(code);
定义了一些其他字面良
还可以是其他类型
let node = t.valueToNode(([1,false,{'x':100}]));
let code = generator(node).code;
console.log(code);

path和node的区别
const updateParamNameVisitor = {
    Identifier(path) {
        if (path.node.name === this.paramName) {
            path.node.name = "x";
        }
        console.log(path);
        path.stop();
    }
};
const visitor = {
    FunctionExpression(path) {
        const paramName = path.node.params[0].name;
        path.traverse(updateParamNameVisitor, {
            paramName
        });
    }
};
traverse(ast, visitor);
// path中的方法
// 获得子节点
// 此二项表达式为a+b+1000
const visitor ={
  BinaryExpression(path){
        // 获得节点属性
      console.log(path.node.left.name);
     const paramName = path.node.left.name;
     console.log(path.node.left.right.operator);
      // 获得节点属性
       console.log(path.get('left'))
      // 判断path对象的类型
      const aa = path.get('right').isNumericLiteral();
      console.log(path.get('right'))
      // path转换为js代码

    };

}
};
function aa (a,b){
    var cc =1;
    return '我是sm'
};
const visitor = {
    let obj = {
    n: 'demo',
    add: function (a, b) {
      var cc = 11;
        return a + b + 1000;
    },
    mul: function (a, b) {
        return a * b + 1000;
    },
};

    ReturnStatement(path) {
        // console.log(path.node);
        //直接打印属性
        console.log( path.node.argument.value);
        //直接替换节点属性
        path.node.argument.value  = t.identifier('aaaasb');
    }
    // 节点一换一
    BinaryExpression(path){
        path.replaceWith(t.valueToNode(1111))
    }
    // 节点多换一('一个节点替换成了两个节点')
   ReturnStatement(path){
       path.replaceWithMultiple([ t.returnStatement(),t.expressionStatement(t.valueToNode('rewafsaafda')),
       ]);

   }
    // 用字符串来替换节点
    ReturnStatement(path){
        let argumentPath = path.get('argument');
        argumentPath.replaceWithSourceString(
            '1aaaa'
        );
        path.stop()
    }
    // 删除节点
    ReturnStatement(path){
        path.remove()
    }
    // 插入节点
    ReturnStatement(path){
        path.insertBefore(t.expressionStatement(t.valueToNode("qqqqq" )))
        path.insertAfter(t.expressionStatement(t.valueToNode('After!')))
    }

    //父级path
    ReturnStatement(path){
        //向上逐层遍历父级节点,当回调函数返回true时,输出当前path对象
        console.log(path.findParent((p) => p.isObjectExpression()).toString())
        //与findParent类似,多了一个遍历当前节点
        path.find();
        //向上查找最接近的父函数,返回一个path对象
        path.getFunctionParent();
        //向上查找最接近的语句,返回一个path对象
        path.getStatementParent()
    }

    //同级path。引入container概念,path相当于数组,container相当于一个数组,listkey是数组的名字,key是在节点当中的位置。badu = ['11','222']
    ReturnStatement(path){
        //判断是否有同级节点。注意数组有且只有一个成员时返回true
       console.log(path.inList);
        //获得容器;获得当前节点在容器中的索引;获得容器名
        console.log(path.container);
        console.log(path.listKey);
        console.log(path.key);
        //获得同级节点的path对象
        path.getSibling('VariableDeclarator');
        //容器内节点的前后插入节点
        path.parentPath.unshiftContainer('body',[t.expressionStatement(t.stringLiteral('Before1')),t.expressionStatement(t.stringLiteral('Before2'))]);
        console.log(path.parentPath.pushContainer('body',t.expressionStatement(t.stringLiteral('After'))))

    }

};

const visitor = {
    //scope详解
    //获得标识符的作用域。可以看出来e的作用范围就在add函数中
    Identifier(path){
        if (path.node.name === 'e'){
            console.log(generator(path.scope.block).code)
        }
    }
   // 获得函数的作用域。只有一个函数
    FunctionDeclaration(path){
        console.log(generator(path.scope.parent.block).code)
    }
    //获取标识符的绑定。接受一个字符串并获取其的绑定,如果传的值应用不到或者应用不到就会返回undefined
    FunctionDeclaration(path){
        //kind:表明了a是一个参数,并不代表是当前domo函数的参数,实际上在js源码种a是add函数的参数(当函数中局部变量和全局变量重名时,使用局部变量)
       // referenced:当前标识符是否被引用references当前标识符被引用的次数
        //constant:是否是常量
        //scope:作用域
        let bind = path.scope.getBinding('a');
        console.log(bind);
        console.log('12');
        //作用域参数获取
        let ba = path.scope.getBinding('a');
        let bo = path.scope.getBinding('demo');
        console.log(ba.referenced);
        console.log(ba.references);
        console.log(generator(ba.scope.block).code);
        console.log(generator(bo.scope.block).code);
        //获取当前节点自己的一个绑定,不包含父级
        path.traverse({Identifier(p){
            let name = p.node.name;
            console.log(name,!!p.scope.getOwnBinding(name));
            }})


    }


};

traverse(ast, visitor);
console.log(generator(ast).code);
//一般使用getBinding替代getOwnBinding,只需加上作用域判断来只取当前节点的标识符绑定

function TestOwnBinding(path){
    path.traverse({
        Identifier(p){
            let name = p.node.name;
            let binding = p.scope.getBinding(name);

            binding && console.log( name, generator(binding.scope.block).code == path + '' );
            // path.stop()
        }
    })
}


traverse(ast,{
    FunctionExpression(path){
        TestOwnBinding(path);
    }
});
//referencePaths,constantViolations
//引用标识符的节点全部存放在referencePaths种
//修改标识符的几点全部存放在constantViolations
traverse(ast,{
    FunctionDeclaration(path){
        let binding = path.scope.getBinding('a');
        binding.scope.traverse(binding.scope.block,{
            AssignmentExpression(p) {
                if (p.node.left.name == 'a'){
                    p.node.right = t.numericLiteral(50444440)
                }
            }
        })
    }
});
console.log(generator(ast).code);
//标识符重命名
traverse(ast,{
   Identifier(path){
       //直接命名
       let binding = path.scope.getBinding('b');
       console.log(generator(binding.scope.block).code);
       binding.scope.rename('b','x');
       console.log('123');
        console.log(generator(binding.scope.block).code);
       //直接命名可能造成标识符冲突用generateUidIdentifier即可避免
      	// path.scope.generateUidIdentifier("uid");
      	// console.log('123');
       console.log(path.scope.generateUidIdentifier('abc').name)

   }
});
 console.log(generator(ast,).code);


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值