AST之path常用属性和方法总结笔记

news2024/11/26 2:01:03

文章目录

    • 1. path常用属性总结
      • 1.1 path.node
      • 1.2 path.scope
      • 1.3 path.parentPath
      • 1.4 path.parent
      • 1.5 path.container
      • 1.6 path.type
      • 1.7 path.key
    • 2. path常用方法总结
      • 2.1 path.toString
      • 2.2 path.replaceWith
      • 2.3 path.replaceWithMultiple
      • 2.4 path.remove
      • 2.5 path.insertBefore
      • 2.6 path.insertAfter
      • 2.7 path.traverse

这一块儿可参考的总结资料不多,参考着蔡老板的文章学习一下,做下记录总结

1. path常用属性总结

path相关的源代码在这个js文件中,大家可以直接照着源码学习:

\node_modules\@babel\traverse\lib\path

这里选出部分常用的属性供大家参考。

path的属性定义:

class NodePath {
  constructor(hub, parent) {
    this.contexts = [];
    this.state = null;
    this.opts = null;
    this._traverseFlags = 0;
    this.skipKeys = null;
    this.parentPath = null;
    this.container = null;
    this.listKey = null;
    this.key = null;
    this.node = null;
    this.type = null;
    this.parent = parent;
    this.hub = hub;
    this.data = null;
    this.context = null;
    this.scope = null;
  }
  ......
}

1.1 path.node

表示当前path下的node节点,通常写插件,函数体的第一行代码就是:

let {node,scope} = path;

1.2 path.scope

表示当前path下的作用域,这个也是写插件经常会用到的。

具体的可以参考蔡老板这篇文章 : Scope和Binding常用方法及属性总结

scope相关:
1.scope.block   

	表示当前作用域下的所有node,参考上面的 this.block = node;

2.scope.dump()  

	输出当前每个变量的作用域信息。调用后直接打印,不需要加打印函数

3.scope.crawl()   

	重构scope,在某种情况下会报错,不过还是建议在每一个插件的最后一行加上。

4.scope.rename(oldName, newName, block)   

	修改当前作用域下的的指定的变量名,oldname、newname表示替换前后的变量名,为字符串。注意,oldName需要有binding,否则无法重命名。

5.scope.traverse(node, opts, state)

 	遍历当前作用域下的某些(个)插件。和全局的traverse用法一样。

6.scope.getBinding(name)

	获取某个变量的binding,可以理解为其生命周期。包含引用,修改之类的信息

Binding:
目前我看到的只有 变量定义 和 函数定义 拥有binding,其他的获取binding都是undefined。

let binding = scope.getBinding(name);
例如:
var a = 123; 这里的 a 就拥有 binding。

而 function test(a,b,c) {};

函数名test以及形参a,b,c均拥有 binding。


1.binding.path

	用于定位初始拥有binding的path;

2.binding.constant

	用于判断当前变量是否被更改,true表示未改变,false表示有更改变量值。

3.binding.referenced

	用于判断当前变量是否被引用,true表示代码下面有引用该变量的地方,false表示没有地方引用该变量。注意,引用和改变是分开的。

4.binding.referencePaths

	它是一个Array类型,包含所有引用的path,多用于替换。

5. binding.constantViolations

	它是一个Array类型,包含所有改变的path,多用于判断。

例如我们可以利用Scope.getBinding()方法来获取Binding对象, 判断其引用情况来对语法树进行修改,

小例子,需求:想对以下js代码进行修改,删除所有定义了, 却从未使用的变量

var a = 1;
var b = 2;
function squire(){
  var c = 3;
  var d = 4;
  return a * d;
  var e = 5;
}
var f = 6;

可以这样写插件:

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;

const jscode = `
var a = 1;
var b = 2;
function squire(){
  var c = 3;
  var d = 4;
  return a * d;
  var e = 5;
}
var f = 6;
`;
let ast = parser.parse(jscode);
const visitor = {
    VariableDeclarator(path)
    {
        const func_name = path.node.id.name;
        const binding = path.scope.getBinding(func_name);
        // 如果变量没有被引用过,那么删除也没关系
        //   此处不能用有无修改过进行判断,因为没有被修改过并不意味着没用
        if(binding && !binding.referenced){
            path.remove();
        }
    },
}


traverse(ast, visitor);
console.log(generator(ast)['code']);

运行出来的结果:

在这里插入图片描述

1.3 path.parentPath

用于获取当前path下的父path,多用于判断节点类型。

1.4 path.parent

用于获取当前path下的父node,多用于判断节点类型。其中:

path.parent == path.parentPath.node;//这两者是等价的

1.5 path.container

用于获取当前path下的所有兄弟节点(包括自身),container翻译过来是容器的意思,它是一个Array类型,可以写个简单的插件来看看效果:

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
let jscode = "var a = 1,b = 2;var c = 3;";
let ast = parser.parse(jscode);
const getcontainer  =
{
  VariableDeclarator(path)
  {
      console.log(path.toString())
      console.log(path.container.length);
  }
}

traverse(ast,getcontainer);

在这里插入图片描述

代码中a,b互为兄弟节点,c只有一个节点,所以结果显示2,2,1。

这里要注意,是获取所有的所有兄弟节点(包括自身),不是path类型。

1.6 path.type

用于获取当前path的节点类型,写个简单的插件来看看效果 :

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
let jscode = "var a = 1 + 3;";
let ast = parser.parse(jscode)
const getType  =
{
  "VariableDeclarator|BinaryExpression|Identifier"(path)
  {
      console.log(path.toString());
      console.log(path.type);

  }
}

traverse(ast,getType);

在这里插入图片描述

1.7 path.key

用于获取当前path的key值,key通常用于path.get函数,当然还有更多的用法等待大家去挖掘 ,写个简单的插件来看看效果 :

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;


let jscode = "var a = 1 + 2;";
let ast = parser.parse(jscode)
const getPathKey  =
{
  "VariableDeclarator|BinaryExpression|Identifier"(path)
  {   
      console.log('path:',path.toString());
      console.log(path.key);

  }
}

traverse(ast,getPathKey);

在这里插入图片描述

第一个遍历的 path 是 VariableDeclarator 类型,对应源代码 是 a = 1+2 ;它的父节点是 VariableDeclaration 类型,而它处在 declarations 的第0的位置,因此输出是 0;

第二个遍历的 path 是 Identifier类型,对应源代码 是 a ;它是 id 节点,因此它的输出是 id;

第三个遍历的 path 是 BinaryExpression 类型,对应源代码 是 1+2 ;它是 init 节点,因此它的输出是 init。

2. path常用方法总结

2.1 path.toString

用来获取当前遍历path的js源代码,调用方式:

let sourceCode = path.toString();

这个非常有用,经常用于定于插件遍历的path,以便分析问题。

如果想通过 path.node来获取源代码,可以使用 generator 函数来获取:

let sourceCode = generator(path.node).code;

2.2 path.replaceWith

(单)节点替换函数,调用方式:

path.replaceWith(newNode);

实参一般是 node 类型,即将当前遍历的path替换为 实参里的 新节点。

注意,它不能用于 Array 的替换,即实参不能是Array类型。

2.3 path.replaceWithMultiple

(多)节点替换函数,调用方式:

path.rreplaceWithMultiple(ArrayNode);

实参一般是 Array 类型,它只能用于 Array 的替换。

即所有需要替换的节点在一个Array里面,常见的替换如 Block 类型节点里的 body.body。

2.4 path.remove

节点的删除,调用方式:

path.remove();

直接调用即可将当前遍历的所有符合条件的路径全部删除,所以使用的时候需要注意。

2.5 path.insertBefore

在当前节点前面插入新的节点。调用方式:

path.insertBefore(newNode);

2.6 path.insertAfter

在当前节点后面插入新的节点。调用方式:

path.insertAfter(newNode);

2.7 path.traverse

在当前节点下遍历其他的节点,比如有如下for循环:

for(;;)
{
  a = b;
  b = c;
  c =d;
}

想要遍历这个for循环下面的 赋值语句,可以借助该函数进行遍历:

const visitFor = 
{
  ForStatement(path)
  {
    ......
    
    path.traverse({
      AssignmentExpression(_path)
      {
        ......
      },
    }),
    
    ......
  },
}

文章到此结束,感谢您的阅读,下篇文章见!

AST学习课程推荐:
蔡老板和风佬课程:AST入门实战+零基础JavaScript补环境课程
也可以看蔡老板的知识星球学习:AST入门与实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/377507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TypeScript 常用知识

「 推荐一个学习 ts 基础的专栏,满满的干货:typeScript 」 1、为什么推荐使用 TypeScript 【】ts 是 js 的超集,包含 js 的所有元素 【】ts 通过对代码进行类型检查,可以帮助我们避免在编写 js 时经常遇到令人痛苦的错误 【】强…

第六节 方法

方法 方法是一种语法结构。 方法的作用: 1.提高代码的复用性 2.让程序逻辑更加清晰 方法定义的完整格式: 修饰符 返回值类型 方法名(形参列表){ 方法体的代码(需要执行的功能代码) return 返回值&#xff…

Unity性能优化: 性能优化之内存篇

前言 本文和传统的内存优化不一样,不是讲如何降低内存占用,而是讲编程开发中要注意的内存问题以及一些内存技术的演变与原理。 对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀 1: Application进程…

maven项目无法解析插件

发现问题使用IDEA创建Maven项目时,报错无法解析插件 org.apache.maven.plugins:maven-clean-plugin这里使用的是IDEA捆绑的Maven插件解决方案查看Maven配置打开用户设置文件settings.xml,在其中加入如果该路径下没有此文件,可以自己创建一个。…

软件分析笔记02---Intermediate Representation

整体contents compiler (source code ——> machine code) non-trivial非平凡的 经过 语义分析->语法分析->类型检查等各种trivial的分析(前端),生成中间代码IR->进行non-trivial的分析(及静…

Linux 基础介绍-基础命令

文章目录01 学习目标02 Linux/Unix 操作系统简介2.1 Linux 操作系统的目标2.2 Linux 操作系统的作用2.3 Unix 家族历史2.4 Linux 家族历史2.5 Linux 和Unix 的联系2.6 Linux 内核介绍2.7 Linux 发行版本2.8 Unix/Linux 开发应用领域介绍03 Linux 目录结构3.1 Win 和Linux 文件系…

C++之入门之引用,内联函数

一、引用 1、引用的概念 在C中,引用的本质其实就是给一个已经存在的变量”起别名“。也就是说,引用与它所引用的对象共用一块空间。(同一块空间的多个名字) 就比如说,李逵又叫黑旋风,而黑旋风就是指李逵…

线程安全实例分析

一、变量的线程安全分析 成员变量和静态变量是否线程安全? ● 如果它们没有共享,则线程安全 ● 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况 —— 如果只有读操作,则线程安全 —— 如果有读写操作&am…

STM32学习笔记-USART串口通信+与野火STM32F407板载ESP8266进行通信

文章目录STM32USART介绍STM32USART框图第一部分第二部分第三部分发送器时序图接收器第四部分软件部分:STM32通过USART与板载ESP8266通讯实验板载WIFI模块电路图实现方式:第一步:配置USART1和USART3的GPIO及其中断第二步:通过中断服…

(2023版)零基础入门网络安全/Web安全,收藏这一篇就够了

由于我之前写了不少网络安全技术相关的文章和回答,不少读者朋友知道我是从事网络安全相关的工作,于是经常有人私信问我: 我刚入门网络安全,该怎么学? 要学哪些东西? 有哪些方向? 怎么选&#x…

智慧渔业海洋鱼类捕捉系统

我国的水产捕捞业可分为海洋捕捞、远洋捕捞和淡水捕捞三类,其中淡水渔业是指在淡水水域进行捕捞、养殖和加工淡水水产品的社会生产领域。近年来,随着经济水平的提高和淡水渔业的快速发展,捕捞业规模不断壮大。尽管渔业已从单纯的捕捞发展为系…

卷麻了,00后测试用例写的比我还好,简直无地自容......

经常看到无论是刚入职场的新人,还是工作了一段时间的老人,都会对编写测试用例感到困扰?例如: 如何编写测试用例? 作为一个测试新人,刚开始接触测试,对于怎么写测试用例很是头疼,无法…

Apple主推的智能家居是什么、怎么用?一篇文章带你从零完全入门 HomeKit

如果你对智能家居有所了解,那应该或多或少听人聊起过 HomeKit。由 Apple 开发并主推的的 HomeKit 既因为产品选择少、价格高而难以成为主流,又因其独特的优秀体验和「出身名门」而成为智能家居领域的焦点。HomeKit 究竟是什么?能做什么&#…

供应链的有效管理,分析指标有哪些

对于企业而言,供应链是一个很复杂的、体系化的生态系统,从原材料、到供应商、到生产、仓库、物流,最后到达经销商或者最终客户那里,这个链条很长。相关的分析指标也有很多,在这些指标里面也有非常多可以扩展、延申的内…

Android 系统的启动流程

前言:从开机的那一刻,到开机完成后launcher将所有应用进行图标展示的这个过程,大概会有哪一些操作?执行了哪些代码?作为Android开发工程师的我们,有必要好好的梳理一遍。既然要梳理Android系统的启动流程&a…

Python-scatter散点图及颜色大全

# -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as pltplt.rcParams[font.sans-serif][SimHei] plt.rcParams[axes.unicode_minus] False #matplotlib画图中中文显示会有问题,需要这两行设置默认字体plt.xlabel(X) plt.ylabel(Y) plt.xlim…

人工智能——离线情况下自动给视频添加字幕,支持中文,英文,日文等等

前言 最近打开百度网盘,看到播放视频有一个AI字幕功能,心情非常激动,看视频的同时可以看自动生成的字幕,防止听不清视频中人物的话语 然而不是SVIP,我试用过了之后就没有这个功能选项了 我在想,如果随便哪一个“免费”…

Windows 安装RocketMQ

文章目录一、RocketMQ是什么?二、准备工作1.环境要求2.下载与解压3.启动MQ4. 测试是否成功启动三、安装管理端1. 代码下载2. 修改配置文件3. 启动MQ客户端jar包四、rocketMQ代码的使用入门五、问题记录1. 启动mqbroker.cmd没有反应2.消费者重复消费消息一、RocketMQ…

二叉搜索树实现

树的导览 树由节点(nodes)和边(edges)构成,如下图所示。整棵树有一个最上端节点,称为根节点(root)。每个节点可以拥有具有方向的边(directed edges)&#xf…

第51天|LeetCode503.下一个更大元素 II、LeetCode42. 接雨水

1.题目链接:下一个更大元素 II 题目描述: 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序&#…