HOWTO · JavaScript

JavaScript 中的枚举器

在今天的帖子中,我们将学习 JavaScript 中的枚举器。

本页内容

JavaScript 没有传统的内置 Enum 类型。在原生 JavaScript 中,如果位置很重要,可以使用有序数组;也可以创建将名称映射到数值的对象。使用数组查找时,indexOf(searchElement) 会搜索匹配的值。TypeScript 提供了枚举语法,但这些 JavaScript 写法使用的是行为各异的普通数组和对象。

枚举在 JavaScript 中表示什么

枚举为一组固定的相关值赋予有意义的名称。在 JavaScript 中,数组和对象都不会自动具备枚举规则:它们只是普通的可变值。应根据代码访问这些值的方式来选择表示形式。如果需要保持顺序或查找某个值的位置,数组会很方便;如果已知名称并想获取对应数值,对象会更合适。

需要有序值和查找时使用数组

数组会保留值的写入顺序。它的 indexOf 方法会返回第一个匹配项的位置;找不到匹配项时则返回 -1。该方法使用严格相等进行比较,因此类型不同的值不会匹配。如果同一个值出现多次,返回的是它第一次出现的位置。

可选的 fromIndex 参数用于设置搜索起点。非负值表示从该位置开始;负值表示从数组末尾向前计算起点。如果计算出的起点超出最后一个元素,搜索就找不到结果。若负数起点落在数组开头之前,则会从第一个元素开始搜索。完整说明请参阅 indexOf 参考文档。

运行下面的示例。代码和输出块保持原样,其中显示的标签仍为英文:

const operatingSystems = ['Linux', 'macOS', 'Windows', 'Ubuntu'];
console.log(operatingSystems.indexOf('Linux'));

返回的数值是第一个匹配值从零开始计数的位置。处理查找结果时,应先检查是否为 -1,再将结果视为有效位置。数组也可以修改:.push() 可以追加值,而 .map() 会根据转换后的值创建新数组。这些操作不会让数组成为枚举,也不会强制限定其值的集合。

使用循环创建类似枚举的对象

如果调用方需要根据名称查找数值,可以通过循环创建一个对象:属性名取自参数列表,属性值则是各自的位置。这个函数使用 arguments,也就是传统函数中用于收集传入参数的局部集合。下面的示例代码和输出块保持原样,其中显示的标签仍为英文。Enum 在这里是用户定义的函数名,并非 JavaScript 关键字或内置类型。

function Enum() {
  const values = {};
  for (let i = 0; i < arguments.length; i++) {
    values[arguments[i]] = i;
  }
  return values;
}

const operatingSystem = Enum('Linux', 'macOS', 'Windows', 'Ubuntu');
console.log(operatingSystem.Linux);

属性 Linux 对应数值 0,因为它是第一个参数。此版本返回普通对象,因此可以直接使用,无需 new。如果参数名称重复,后续赋值会覆盖该属性之前的值。由于这个对象继承自 Object.prototype,这种简单写法适合名称受控的普通列表;如果名称可能来自不可信或任意输入,请使用空原型对象或 Map。这种映射是单向的:它不会提供从数值反查名称的功能,也不会验证程序其他位置赋予的值。

选择合适的写法并了解其限制

如果顺序很重要,或需要使用 indexOf 搜索值,请使用数组。如果输入是已知名称,而且直接访问属性比搜索数组更清晰,请使用名称到数值的对象。无论采用哪种写法,JavaScript 都不会强制集合保持固定、阻止未知值,也无法保证重新排列条目后数值位置仍然稳定。

对于少量常量,最好将这些值放在一起,并约定初始化后不再修改。如果代码依赖持久化数值、网络格式或多个程序共享的值,请指定明确的数值,并将它们视为接口的一部分;如果从数组位置推导数值,重新排序可能会悄然改变这些数值的含义。TypeScript 枚举可以提供编译时枚举语法,但它们属于 TypeScript 功能,并且会产生各自的 JavaScript 输出行为。如果项目使用 TypeScript 类型系统,可以考虑使用它们;不要误以为原生 JavaScript 数组或对象也提供相同的检查。