在 TypeScript 中,enum
关键字提供了一种定义一组命名常量的方式,这些常量可以作为类型或值使用。枚举最早在 TypeScript 的第一个版本中引入,虽然它们还没有被添加到 JavaScript 中,但它们在 TypeScript 中作为一个独有的运行时特性,展现了强大的功能和一些有趣的行为。
文末有我针对前端面试的多年经验文章,欢迎大家积极发言!!!
枚举特别适用于定义一组有限的、稳定的常量值,帮助开发者编写更加清晰和易于维护的代码。
数值枚举
数值枚举是一种将相关的成员组合在一起,并为它们自动分配从 0 开始的数值的机制。例如,以下是一个定义了 AlbumStatus
枚举的代码:
enum AlbumStatus {
NewRelease,
OnSale,
StaffPick
}
在这个例子中,AlbumStatus.NewRelease
的值为 0,AlbumStatus.OnSale
为 1,依此类推。我们可以通过名称来使用这个枚举:
function logStatus(status: AlbumStatus) {
console.log(status);
// 输出 0
}
logStatus(AlbumStatus.NewRelease);
当然,你也可以为枚举的每个成员指定特定的值:
enum AlbumStatus {
NewRelease = 1,
OnSale = 2,
StaffPick = 3
}
此时,AlbumStatus.NewRelease
的值为 1,AlbumStatus.OnSale
为 2,依次类推。
自动递增的数值枚举
当你只为枚举的某些成员指定了值时,TypeScript 会从最后一个指定值开始自动递增。例如:
enum AlbumStatus {
NewRelease = 1,
OnSale,
StaffPick,
}
在这个例子中,OnSale
会自动分配为 2,StaffPick
则为 3。
字符串枚举
字符串枚举允许你为枚举的每个成员分配字符串值,这在某些场景中会更具表达力:
enum AlbumStatus {
NewRelease = "NEW_RELEASE",
OnSale = "ON_SALE",
StaffPick = "STAFF_PICK",
}
使用字符串枚举后,logStatus
函数将输出字符串值:
function logStatus(status: AlbumStatus) {
console.log(status);
// 输出 "NEW_RELEASE"
}
logStatus(AlbumStatus.NewRelease);
枚举的奇特行为
由于 JavaScript 本身没有 enum
关键字的等效语法,TypeScript 在为枚举制定规则时展现了一些独特的行为。
数值枚举的反向映射
当 TypeScript 编译数值枚举时,会生成一个支持反向映射的对象。这意味着你不仅可以通过枚举名获取数值,还可以通过数值反向获取枚举名。例如:
enum AlbumStatus {
NewRelease,
OnSale,
StaffPick,
}
会被编译为以下 JavaScript 代码:
var AlbumStatus;
(function (AlbumStatus) {
AlbumStatus[AlbumStatus["NewRelease"] = 0] = "NewRelease";
AlbumStatus[AlbumStatus["OnSale"] = 1] = "OnSale";
AlbumStatus[AlbumStatus["StaffPick"] = 2] = "StaffPick";
})(AlbumStatus || (AlbumStatus = {}));
这个代码段为每个枚举值创建了一个对象属性,同时生成了一个值到键的反向映射:
var AlbumStatus = {
0: "NewRelease",
1: "OnSale",
2: "StaffPick",
NewRelease: 0,
OnSale: 1,
StaffPick: 2,
};
因此,当你调用Object.keys(AlbumStatus)
时,将会得到如下输出:
console.log(Object.keys(AlbumStatus));
// ["0", "1", "2", "NewRelease", "OnSale", "StaffPick"]
字符串枚举的简化行为
相比之下,字符串枚举的行为要简单得多。它不会生成反向映射,只保留枚举值:
enum AlbumStatus {
NewRelease = "NEW_RELEASE",
OnSale = "ON_SALE",
StaffPick = "STAFF_PICK",
}
编译后生成的 JavaScript 代码为:
var AlbumStatus;
(function (AlbumStatus) {
AlbumStatus["NewRelease"] = "NEW_RELEASE";
AlbumStatus["OnSale"] = "ON_SALE";
AlbumStatus["StaffPick"] = "STAFF_PICK";
})(AlbumStatus || (AlbumStatus = {}));
调用 Object.keys(AlbumStatus)
只会返回键值:
console.log(Object.keys(AlbumStatus));
// ["NewRelease", "OnSale", "StaffPick"]
这种数值枚举和字符串枚举之间的差异可能会引起一定的困惑。
类型差异
数值枚举和字符串枚举在用作类型时表现不同。让我们用数值枚举来定义 logStatus
函数:
enum AlbumStatus {
NewRelease = 0,
OnSale = 1,
StaffPick = 2,
}
function logStatus(status: AlbumStatus) {
console.log(status);
}
你可以直接传入枚举成员,也可以传入一个对应的数字:
logStatus(AlbumStatus.NewRelease);
logStatus(0);
但是,如果传入一个非枚举成员的数字,TypeScript 会报错:
logStatus(3);
// Argument of type '3' is not assignable to parameter of type 'AlbumStatus'.
相比之下,字符串枚举只允许枚举成员作为类型:
enum AlbumStatus {
NewRelease = "NEW_RELEASE",
OnSale = "ON_SALE",
StaffPick = "STAFF_PICK",
}
function logStatus(status: AlbumStatus) {
console.log(status);
}
logStatus(AlbumStatus.NewRelease);
logStatus("NEW_RELEASE");
// Argument of type '"NEW_RELEASE"' is not assignable to parameter of type 'AlbumStatus'.
字符串枚举的行为更接近其他语言(如 C# 和 Java)中的枚举,但它们与数值枚举的不一致性可能会让人感到困惑。特别是字符串枚举在 TypeScript 中是基于名称(名义)进行比较的,而不是像其他类型那样基于结构进行比较。这意味着即使两个字符串枚举的成员相同,但名称不同,它们也会被视为不同的类型:
enum AlbumStatus2 {
NewRelease = "NEW_RELEASE",
OnSale = "ON_SALE",
StaffPick = "STAFF_PICK",
}
logStatus(AlbumStatus2.NewRelease);
// Argument of type 'AlbumStatus2.NewRelease' is not assignable to parameter of type 'AlbumStatus'.
const 枚举
const enum
允许你在声明枚举时使用 const
关键字,这种枚举在编译时会直接被替换为相应的值,不会生成任何额外的对象。使用 const enum
的代码如下:
const enum AlbumStatus {
NewRelease = "NEW_RELEASE",
OnSale = "ON_SALE",
StaffPick = "STAFF_PICK",
}
编译后,TypeScript 会直接将枚举的值替换到代码中:
let albumStatuses = [
"NEW_RELEASE",
"ON_SALE",
"STAFF_PICK",
];
不过,TypeScript 团队建议在库代码中避免使用 const enum
,因为它们可能会在库的使用者那里表现出不可预测的行为。
总结
在这篇文章中,我们探讨了 TypeScript 枚举的几种形式及其特性:
- 数值枚举 具有反向映射的特性,并且类型不具备强制性,可以用具体值代替。
- 字符串枚举 不具备反向映射,但类型具有强相等性,不会进行结构对比,也不能直接用值替代。
- 常量枚举 在编译后会被替换成具体的值,而不会生成额外的 JavaScript 代码。
通过合理地使用枚举,你可以让代码更加简洁、清晰,同时增强代码的可维护性。
希望这篇文章对你有所帮助!接下来给大家推荐一篇我针对前端面试的多年经验文章,希望大家看完以后都可以领取到心仪的offer哦!
文章:《聊聊前端面试那些事儿》