path.scope 中记录着作用域相关的数据,通过 scope 可以拿到整条作用域链,包括声明的变量和对该声明的引用。
这节我们实现下 scope。
# 思路分析
前面我们实现了 traverse 和 path,能够遍历 AST 和对 AST 增删改了,而 scope 和 path 一样也是遍历过程中记录的信息。
能生成 scope 的 AST 叫做 block,比如 FunctionDeclaration 就是 block,因为它会生成一个新的 scope。
我们把这类节点记录下来,遍历的时候遇到 block 节点会生成新的 scope,否则拿之前的 scope。
scope 中记录着 bindings,也就是声明,每个声明会记录在哪里声明的,被哪里引用的。
遇到 block 节点,创建 scope 的时候,要遍历作用域中的所有声明(VariableDeclaraion、FunctionDeclaration),记录该 binding 到 scope 中。
记录完 bindings 之后还要再遍历一次记录引用这些 binding 的 reference。
基于这种思路我们就能实现 scope 的功能。
# 代码实现
首先,创建 Binding 类和 Scope 类:
class Binding {
constructor(id, path, scope, kind) {
this.id = id;
this.path = path;
this.referenced = false;
this.referencePaths = [];
}
}
class Scope {
constructor(parentScope, path) {
this.parent = parentScope;
this.bindings = {};
this.path = path;
}
registerBinding(id, path) {
this.bindings[id] = new Binding(id, path);
}
getOwnBinding(id) {
return this.bindings[id];
}
getBinding(id) {
let res = this.getOwnBinding(id);
if (res === undefined && this.parent) {
res = this.parent.getOwnBinding(id);
}
return res;
}
hasBinding(id) {
return !!this.getBinding(id);
}
}
bindings 是记录作用域中的每一个声明,同时我们还可以实现 添加声明 registerBinding、查找声明 getBinding、getOwnBinding、hasBidning 的方法。
getOwnBing 是只从当前 scope 查找,而 getBinding 则是顺着作用域链向上查找。
之后我们在 path 里面定义一个 scope 的 get 的方法,当需要用到 scope 的时候才会创建,因为 scope 创建之后还要遍历查找 bindings,是比较耗时的,实现 get 可以做到用到的时候才创建。
get scope() {
if (this.__scope) {
return this.__scope;
}
const isBlock = this.