@babel/types从入门到放弃

背景

我一直在思考一个问题,为什么线上出现问题时总是难以找到原因呢?每一次都是遇到问题之后,开始排查问题,开始根据报错信息直接去翻看源码(我相信一般的公司应该不会把 sourcemap 打一份放到内网里面),于是翻来覆去一顿操作之后找到了问题,然后紧急发布解决问题;然后每一次出现线上故障都是这样的操作,非常之低效;

既然要快速定位线上问题,那就必须对线上代码有所了解;可以说大部分同学都只了解自己所写代码,而并不了解打包之后代码成了什么样子(由于浏览器兼容性问题,打包之后代码可能会面目全非)。我不想成为他们之中的一员,所以一直在看打包之后的代码,以及编译器 babel,即使现在新出了 swc、esbuild,babel 仍然是主流。最近我在看装饰器的编译时犯了难,这里面用到了@babel/types 这个库,当我去查看文档时,它的文档是这样的:

没有示例,也没有解释这个表达式的含义,只给了一些参数,总之这个文档太水了。现在我要让你们忘记这个文档,不要再看这个文档,回想一下第一次学 js 的场景。

js 数据类型

第一天学的一定是 js 的数据类型:

  • Null
  • Undefined
  • Boolean
  • Number
  • String
  • Symbol
  • Object

用 babel 如何生成这些类型的值呢?请大家先准备好 node 环境,安装如下依赖:

  • @babel/generator
  • @babel/types或者@babel/core(我这里用的@babel/core)

大功告成之后,先定义一个函数,将 ast 转换为代码,方便后续打印值;

js 复制代码
const generate = require("@babel/generator").default;

function log(node) {
  const code = generate(node).code;
  console.log("code:", code);
}

创建简单类型值

js 复制代码
const { parse, types } = require("@babel/core");

const _null = types.nullLiteral();
log(_null); // code: null

const boolean = types.booleanLiteral(true);
log(boolean); // code: true

const number = types.numericLiteral(42);
log(number); // code: 42

const string = types.stringLiteral("Hello, World!");
log(string); // code: Hello, World!

总结一下简单数据类型如何创建 ast:

  • Null:types.nullLiteral()
  • Boolean:types.booleanLiteral(true)
  • Number:types.numericLiteral(42)
  • String:types.stringLiteral("Hello, World!")
  • Undefined和 Symbol 类型都没有办法创建

创建Array

object 类型,又分为 Array、Object、Function等等,数组类型最为简单,先实现一个简单的数组吧!实现数据使用:arrayExpression

js 复制代码
const string = types.stringLiteral("Hello, World!");
const number = types.numericLiteral(42);
const boolean = types.booleanLiteral(true);
const array = types.arrayExpression([string, number, boolean]);

log(array); // code: ["Hello, World!",42,true]

创建 Object

对象类型比较复杂,首先要执行objectExpression创建对象,然后objectProperty声明属性:

js 复制代码
const object = types.objectExpression([
  types.objectProperty(types.stringLiteral("name"), types.stringLiteral("John")),
  types.objectProperty(types.stringLiteral("age"), types.numericLiteral(30)),
]);
log(object); // {name:"John",age:30}

创建函数

函数声明包含函数名、参数、函数体,另外一个函数还需要区分是否为 generator 函数,是否为 async 函数,创建函数需要借助functionExpression,有五个参数:

  • id: types.Identifier, 函数名
  • params: (types.Identifier | types.RestElement | types.Pattern)[], 函数参数
  • body: types.BlockStatement, 函数体
  • generator?: boolean, 是否为generator函数
  • async?: boolean,是否为异步函数

函数名和参数名都属于标识符,函数体属于块声明,需要blockStatement创建,执行函数需要callExpression创建,表达式则为expressionStatement

接下来实现一个打印其参数的函数:

js 复制代码
const func = types.functionExpression(
  types.identifier("func"),
  [types.identifier("params")],
  types.blockStatement([
    types.expressionStatement(types.callExpression(types.identifier("console.log"), [types.identifier("params")])),
  ]),
  false,
  false
);
log(func); 
// code:function func(params) {
//   console.log(params);
// }

总结一下创建复杂数据类型都用到了哪些方法:

  • Array:types.arrayExpression()
  • Object:types.objectExpression()、types.objectProperty()
  • Function:types.functionExpression()、types.blockStatement()、types.expressionStatement()、types.callExpression()

js 语法

变量声明variableDeclaration

js 复制代码
const res = types.variableDeclaration("const", [
  types.variableDeclarator(types.identifier("age"), types.numericLiteral(42)),
]);
log(res); // code: const age = 42;

赋值表达式assignmentExpression

赋值表达式首先需要传入操作符,然后就是左值和右值,但是这里如果没有声明则是全局变量

js 复制代码
log(types.assignmentExpression("=", types.identifier("age"), types.numericLiteral(42)));
// code: age = 42

序列表达式sequenceExpression

序列表达式其实就是逗号操作符,比如:(1,2),利用sequenceExpression创建

js 复制代码
log(types.sequenceExpression([types.numericLiteral(1), types.numericLiteral(2)]));
// 2

条件语句ifStatement

js 复制代码
const ifstatement = types.ifStatement(
  types.identifier("name"),
  types.blockStatement([
    types.expressionStatement(
      types.assignmentExpression("=", types.identifier("name.this"), types.stringLiteral("John"))
    ),
  ])
);
log(ifstatement);
// code: if (name) {
//   name.this = "John";
// }

循环语句forStatement

js 复制代码
const forstatement = types.forStatement(
  types.variableDeclaration("let", [types.variableDeclarator(types.identifier("i"), types.numericLiteral(0))]),
  types.binaryExpression(">", types.identifier("i"), types.numericLiteral(10)),
  types.assignmentExpression(
    "=",
    types.identifier("i"),
    types.binaryExpression("+", types.identifier("i"), types.numericLiteral(1))
  ),
  types.blockStatement([
    types.expressionStatement(types.callExpression(types.identifier("console.log"), [types.identifier("i")])),
  ])
);
log(forstatement);
// code: for (let i = 0; i > 10; i = i + 1) {
//   console.log(i);
// }

到这里为止就实现了一些常用数据类型和语法,为以后写 babel 插件打下基础

相关推荐
Json_1817901448015 分钟前
电商拍立淘按图搜索API接口系列,文档说明参考
前端·数据库
风尚云网38 分钟前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
木子020440 分钟前
前端VUE项目启动方式
前端·javascript·vue.js
GISer_Jing42 分钟前
React核心功能详解(一)
前端·react.js·前端框架
捂月1 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
深度混淆1 小时前
实用功能,觊觎(Edge)浏览器的内置截(长)图功能
前端·edge
Smartdaili China1 小时前
如何在 Microsoft Edge 中设置代理: 快速而简单的方法
前端·爬虫·安全·microsoft·edge·社交·动态住宅代理
秦老师Q1 小时前
「Chromeg谷歌浏览器/Edge浏览器」篡改猴Tempermongkey插件的安装与使用
前端·chrome·edge
滴水可藏海1 小时前
Chrome离线安装包下载
前端·chrome
endingCode1 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript