操作AST主要方法

添加节点

替换节点

删除节点

添加节点

function visitCallback(node, parent, key, index) {
  node.foo = createNewNode();
}

如果节点是一个数组:

function visitCallback(node, parent, key, index) {
  node.foo.push(createNewNode());
}

如果要将节点添加为兄弟节点,那就需要访问它的父节点:

function visitCallback(node, parent, key, index) {
  // 插入到最前
  parent[key].unshift(createNewNode());
  // 添加到最后
  parent[key].push(createNewNode());
  // 插入到当前节点之后
  parent[key].splice(index + 1, 0, createNewNode());
  // 添加到当前节点之前
  parent[key].splice(index, 0, createNewNode());
}

替换节点

要将当前节点替换为另一个节点,需要更新当前节点的父节点的 key 属性
function visitCallback(node, parent, key, index) {
  parent[key] = updatedNode();
}

如果父节点是数组:

function visitCallback(node, parent, key, index) {
  parent[key][index] = updatedNode();
}

删除节点

要删除当前节点,需要删除当前节点父节点的 key 属性
function visitCallback(node, parent, key, index) {
  delete parent[key];
}

如果父节点是数组:

function visitCallback(node, parent, key, index) {
  parent[key].splice(index, 1);
}

注意:所有操作完成后,需要确保遍历的正常进行,对于作为其父键的属性的节点,添加、替换和删除它们通常是可以的。除了替换操作,你可能需要重新访问“当前节点”,即新的替换节点。

但是,对于数组中的节点,需要特别注意更新循环的数组索引:

function visit(ast, callbackMap) {
  function _visit(node, parent, key, index) {
    // ...
    if (Array.isArray(child)) {
      for (let j = 0; j < child.length; j++) {
        _visit(child[j], node, key, j);
        if (hasRemoved()) {
          j--;
        }
      }
    }
    // ...
  }
}

怎么判断当前节点是否被删除呢?

function visit(ast, callbackMap) {
  function _visit(node, parent, key, index) {
    let _hasRemoved = false;
    const _this = {
      //不需要node,parent
      remove() {
        _hasRemoved = true;
        // 继续删除当前节点
      },
    };

    // ...
    if (nodeType in callbackMap) {
      callbackMap[nodeType].call(_this, node, parent, key, index);
    }
  }
}

function visitCallback(node, parent, key, index) {
  this.remove();
}
如果觉得我的文章对你有用,请随意赞赏