scope 作用域
scope 提供了一些属性和方法
使用的例子
const a = 1000; let b = 2000; let obj = { name: 'haha', add: function (a) { a = 400; b = 300; let e = 700; function demo() { let b = 600; } demo(); return a + a + b + 1000 + obj.name } } obj.add(100)
获取’标识符’作用域
path.scope.block()
可以获取标识符 作用域
,返回Node 对象
visitor = { Identifier(path) { if (path.node.name === 'e') { // e 的作用域就是 整个 函数 console.log(generator(path.scope.block).code) } } } traverse(ast, visitor) /* function (a) { a = 400; b = 300; let e = 700; function demo() { let b = 600; } demo(); return a + a + b + 1000 + obj.name; } */
但有时候
作用域输出的范围
与 实际不符合
,就需要获取父节点的作用
visitor = { FunctionDeclaration(path) { // 函数声明 只有 demo() 满足要求 console.log(generator(path.scope.block).code) } } traverse(ast, visitor) /* function demo() { let b = 600; } */
demo()
的作用域
实际上
应该是 整个 add() 的范围
,这时就与实际输出不符合
,就需要 获取 父节点的作用域
visitor = { FunctionDeclaration(path) { console.log(generator(path.scope.parent.block).code) } } traverse(ast, visitor) /* function (a) { a = 400; b = 300; let e = 700; function demo() { let b = 600; } demo(); return a + a + b + 1000 + obj.name; } */
path.scope.getBinding
获取
标识符
对应的绑定对象 (Binding)
visitor = { FunctionDeclaration(path) { let binding = path.scope.getBinding('a') console.log(binding) } } traverse(ast, visitor) /* Binding { identifier : Node {type: "Identifier", start: 83, end: 84, loc: SourceLocation, name: "a"}, // 标识符 的 Node对象 scope : Scope {uid: 1, path: NodePath, block: Node, labels: Map(0), inited: true, ...}, // 对应 标识符的 scope 对象 path : NodePath {contexts: Array(0), state: Object, opts: Object, _traverseFlags: 0, skipKeys: null, ...}, // 对应标识符的 Path 对象 kind : "param", // 该标识符类型 这里表示 是一个参数 constantViolations : Array(1) [NodePath], // 存放 修改了 该标识符的节点 constant : false, // 是否是常量 referencePaths : Array(2) [NodePath, NodePath], // 引用了 该标识符的 NodePath 数组 referenced : true, // 该标识符 是否被引用 references : 2, // 该标识符的 引用次数 hasDeoptedValue : false, // hasValue : false, value : null } */
referencePaths & constantViolations
这两个是
Binding
对象里的属性
/* Binding { ... referencePaths : Array(2) [NodePath, NodePath], // 引用了 该标识符的 NodePath 数组,没有引用时为 空数组 constantViolations : Array(1) [NodePath], // 存放 修改了 该标识符的节点,没有被修改时为 空数组 ... } */
遍历作用域
有两种方式遍历作用域
path.scope.traverse({})
binding.scope.traverse({})
: 推荐
// path.scope.traverse({}) visitor = { FunctionExpression(path) { let binding = path.scope.traverse({ Identifier(path){ console.log('') } }) } } traverse(ast, visitor) // binding.scope.traverse({}) 推荐 visitor = { FunctionExpression(path) { let binding = path.scope.getBinding('a') binding.scope.traverse({ Identifier(path){ console.log('') } }) } } traverse(ast, visitor)
把例子 函数中的 a=400
改成 a=60
visitor = { FunctionExpression(path) { let binding = path.scope.getBinding('a') binding.scope.traverse(path.scope.block, { // 遍历 a的 作用域 AssignmentExpression(path) { if (path.node.left.name === 'a'){ path.node.right = type.valueToNode(60) } } }) } } traverse(ast, visitor)
标识符重命名
binding.scope.rename()
该方法,会重命名所有
引用了该标识符的 地方
visitor = { FunctionExpression(path) { let binding = path.scope.getBinding('b') binding && binding.scope.rename('b','xx') } } traverse(ast, visitor) /* const a = 1000; let b = 2000; let obj = { name: 'haha', add: function (xx) { // a --> xx xx = 400; // 函数的参数 a --> xx b = 300; let e = 700; function demo() { let b = 600; } demo(); return xx + xx + b + 1000 + obj.name; } }; obj.add(100); */
随机生成标识符
path.scope.generateUidIdentifier('_0xoO').name
该方法 会生成一个字符串 且不会重复
可以用来实现 简单的 标识符 混淆
,也可以用来 反混淆,使变量看起来 好分析
visitor = { Identifier(path) { path.scope.rename(path.node.name,path.scope.generateUidIdentifier('_0xoO').name) } } traverse(ast, visitor) /* const _0xoO = 1000; let _0xoO15 = 2000; let _0xoO18 = { name: 'haha', add: function (_0xoO14) { _0xoO14 = 400; _0xoO15 = 300; let _0xoO9 = 700; function _0xoO12() { let _0xoO11 = 600; } _0xoO12(); return _0xoO14 + _0xoO14 + _0xoO15 + 1000 + _0xoO18.name; } }; _0xoO18.add(100); */
scope 其他方法
path.scope.hasBinding('a')
返回值是bool 值
,作用是 查询该作用域
是否有该标识符的 绑定
该方法 可以用 path.scope.getBinding('a')
代替,只是 该方法的返回值是 undefined
和 Binding {}
visitor = { Identifier(path) { console.log(path.scope.hasBinding('a')) } } traverse(ast, visitor) /* true ... */
path.scope.getAllBindings()
获取该节点的所有绑定,返回的对象
以标识符名
为属性名
,对应的 属性是Binding {..}
visitor = { Identifier(path) { console.log(path.scope.getAllBindings()) } } traverse(ast, visitor) /* Object {a: Binding {..}, b: Binding {..}, obj: Binding {..}} */
path.scope.hasReference('a')
查询当前节点
是否有 标识符a
的引用,返回值是true / false
版权声明:《 【AST 基础】四、AST 的 scope 作用域 》为明妃原创文章,转载请注明出处!
最后编辑:2022-4-9 09:04:58