TypeScript 枚举是开发中常用的定义常量的方式,但在编译为 JavaScript 后,字符串枚举与数字枚举的运行时行为存在显著差异。理解这些差异有助于避免潜在的 Bug 并优化代码体积。
1. 创建并检查数字枚举的运行时结构
数字枚举在运行时会被编译成一个双向映射的对象,既可以通过成员名获取值,也可以通过值获取成员名。
创建一个名为 NumericEnum.ts 的文件,并输入以下代码:
enum NumericStatus {
Offline, // 默认值为 0
Online, // 默认值为 1
Busy // 默认值为 2
}
console.log(NumericStatus.Online); // 输出值
console.log(NumericStatus[1]); // 输出键名
运行 tsc NumericEnum.ts 命令编译代码。打开生成的 NumericEnum.js 文件,观察其结构:
var NumericStatus;
(function (NumericStatus) {
NumericStatus[NumericStatus["Offline"] = 0] = "Offline";
NumericStatus[NumericStatus["Online"] = 1] = "Online";
NumericStatus[NumericStatus["Busy"] = 2] = "Busy";
})(NumericStatus || (NumericStatus = {}));
分析上述代码:
TypeScript 编译器生成了一个 NumericStatus 对象。每一行赋值语句,例如 NumericStatus[NumericStatus["Offline"] = 0] = "Offline";,执行了两个操作:
- 将字符串
"Offline"映射到数字0。 - 将数字
0映射回字符串"Offline"。
执行 node NumericEnum.js,控制台将输出 1 和 "Online"。这证明了数字枚举支持反向映射。
2. 创建并检查字符串枚举的运行时结构
字符串枚举在运行时只会生成一个单向映射的对象。每个成员必须显式初始化字符串值,且不支持通过值反向查找成员名。
创建一个名为 StringEnum.ts 的文件,并输入以下代码:
enum StringStatus {
Offline = "OFFLINE",
Online = "ONLINE",
Busy = "BUSY"
}
console.log(StringStatus.Online); // 输出值
console.log(StringStatus["ONLINE"]); // 尝试反向查找
运行 tsc StringEnum.ts 命令编译代码。打开生成的 StringEnum.js 文件,观察其结构:
var StringStatus;
(function (StringStatus) {
StringStatus["Offline"] = "OFFLINE";
StringStatus["Online"] = "ONLINE";
StringStatus["Busy"] = "BUSY";
})(StringStatus || (StringStatus = {}));
分析上述代码:
生成的代码仅包含简单的属性赋值,没有生成反向映射的逻辑。
执行 node StringEnum.js。控制台将输出 "ONLINE" 和 undefined。当尝试通过值 "ONLINE" 去查找键名时,结果是未定义的,说明该属性不存在于对象中。
3. 对比运行时差异与内存影响
通过上述两个实验,我们可以总结出两者在运行时的核心区别。
| 特性 | 数字枚举 | 字符串枚举 |
|---|---|---|
| 映射方向 | 双向映射 (Name <-> Value) | 单向映射 (Name -> Value) |
| 反向查找 | 支持 Enum[Value] |
不支持,返回 undefined |
| 代码体积 | 较大 (需存储额外的键值对) | 较小 (仅存储必要的属性) |
| 初始化 | 可自增或显式赋值 | 必须显式赋值字符串 |
| 调试体验 | 在日志中常显示为无意义的数字 | 直接显示有意义的字符串文本 |
4. 处理位运算与特殊场景
数字枚举支持位运算,常用于权限控制等场景,而字符串枚举则完全不支持。
创建一个名为 BitwiseEnum.ts 的文件,并输入以下代码:
enum Permission {
Read = 1, // 0001
Write = 2, // 0010
Execute = 4 // 0100
}
// 计算组合权限
let userPermission = Permission.Read | Permission.Write;
// 检查权限
const hasWrite = (userPermission & Permission.Write) === Permission.Write; // true
const hasExecute = (userPermission & Permission.Execute) === Permission.Execute; // false
console.log(`Has Write: ${hasWrite}`);
console.log(`Has Execute: ${hasExecute}`);
注意:如果尝试将字符串枚举用于位运算,逻辑将无法按预期工作。例如,"A" | "B" 的结果并不是一个易于检查的数值,而是一个字符串的按位或结果。
5. 根据场景选择枚举类型
根据实际需求选择合适的枚举类型是关键。
-
选择数字枚举,如果:
- 需要利用位运算组合状态。
- 需要根据值反向获取对应的名称(例如在 API 返回错误码
404时,直接ErrorCode[404]获取 "NotFound" 描述)。 - 枚举成员值本身无业务含义,仅作为标识。
-
选择字符串枚举,如果:
- 需要可读性强、易于调试的日志输出。
- 代码体积是主要关注点(无需生成反向映射代码)。
- 值需要与后端 API 或数据库中的字符串字段严格对应。
- 不需要任何形式的位运算或反向映射。
总结核心差异:数字枚举在运行时是一个“双向字典”,提供了灵活性但增加了体积;字符串枚举则是一个“单向字典”,结构简单、语义清晰且节省空间。

暂无评论,快来抢沙发吧!