在 go
语言中,存在一个特殊的常量值 iota
,通常用来快速定义枚举值,在编译时将会被替换成基本的数字,基本用法如下:
package main
import "fmt"
// 声明了3个常量,并且值分别是 0 1 2
const (
A = iota
B
C
)
func main() {
fmt.Println(A, B, C)
}
运行结果为:
0 1 2
写起来非常方便,只需要写一次iota
,后面就会自动自增赋值。
而在 javascript
中,如果要定义枚举值,通常需要一个一个的赋值,例如:
const A = 0, B = 1, C = 2;
console.log(A, B, C);
可以看到,写起来有一些繁琐,需要手动维护每一个枚举值,还可能写重复
如果是 typescript
中,可以使用 const enum
来实现相同的功能,具体写法如下:
const enum SomeEnum {
A,
B,
C,
};
console.log(SomeEnum.A, SomeEnum.B, SomeEnum.C); // 输出 0 1 2
那如果不使用 typescript
,该如何实现一个类似于 go
中的 iota
,同时又能满足js语法的效果呢?
本文在此将实现一种使用Babel进行简单语法转换的方式,实现上面描述的功能,仅用于学习,毕竟这个并没有什么用,你可能永远不会在项目中使用。😛
语法设计
首先我们需要找到一种js语法形式,能快速定义多个常量,并且只需要赋值一次,那在js中,解构赋值
就能满足我们的需要
const [A, B, C] = iota;
那聪明的你可能已经想到了,这里的 iota
如果定义成可迭代对象,不也能满足需求吗?还是原生的js写法,连Babel都省了
const iota = {
[Symbol.iterator]() {
let v = 0;
return {
next: () => ({ value: v++ })
}
}
};
const [A, B, C] = iota;
const [D, E, F] = iota;
console.log(A, B, C); // 输出 0 1 2
console.log(D, E, F); // 输出 0 1 2
🤔。。。本文结束🎉。。。
可是作为一个有理想的前端人,不是很想在项目中定义枚举值时,用了运行时计算的方式,还有就是要完成本文的标题任务(主要是这个),在此,将会介绍使用Babel插件实现。
希望转换的成果
希望通过转换后,得到如下的效果,从代码层面上消除 iota
标识符,同时将所有由iota
声明的常量进行自增赋值:
const A = 0, B = 1, C = 2;
插件编写
废话不多说,接下来将直接在 astexplorer.net
上写一个这个插件
转换前的代码如下,在此特地在一个 const
语句中声明了2个iota
语句,并且第一个赋值缺省了D
这个标识符
export const [A, B, C, , E] = iota, [F, G, H] = iota;
那我们的插件将按照下面的方式进行编写:
export default function ({ types: t }) {
// 用于判断是否是 iota 赋值
const filterIota = (declarator) => t.isIdentifier(declarator.init, { name: "iota" });
return {
name: "babel-plugin-iota",
visitor: {
// 仅处理变量声明语句
VariableDeclaration(nodePath) {
const { node } = nodePath;
// 如果不是 const 声明,则跳过
if (node.kind !== "const") {
return;
}
// 如果不存在 iota 赋值,则跳过
if (!node.declarations.some(filterIota)) {
return;
}
// 新的定义语句
const newDeclarations = [];
node.declarations.forEach((it) => {
// 如果是 iota 赋值语句
if (filterIota(it)) {
// 校验是数组解构赋值
t.assertArrayPattern(it.id);
// 展开每个标识符
it.id.elements.forEach((el, idx) => {
// 可能存在缺省声明
if (el) {
// 校验必须是标识符,不能是其它形式
t.assertIdentifier(el);
// 转换成 XX = 数字 的形式
newDeclarations.push(t.variableDeclarator(el, t.numericLiteral(idx)));
}
});
} else {
// 其它形式的赋值语句保持原样
newDeclarations.push(it);
}
});
// 替换原 const 变量声明语句
nodePath.replaceWith(t.variableDeclaration("const", newDeclarations));
}
}
};
}
以下是一个转换的效果图
至此,我们使用Babel插件完成了类似go语言中的iota定义枚举值的一个功能
真·完结撒花🎉