在 Javascript 中使用枚举

文章目录

这篇文章将解释如何在 Javascript 中实现和使用枚举(或枚举类型)。

枚举是包含有限数量固定值的类型,这与 Number 或 String 等可以包含广泛值的类型相反。

这在许多情况下都很有用:例如,在描述温带气候的季节时,您需要将选项限制为某些可能的值:Summer、Winter、Spring和Autumn。

让我们看看实现此类数据的不同方法:

将枚举定义为对象键

对于枚举的基本实现,我们可以定义一个对象来封装枚举类型,并为每个枚举值分配一个键。

例如,我们可以将季节表示为对象,每个季节作为键,代表字符串作为值:

const Seasons = {
	Summer: "summer",
	Autumn: "autumn",
	Winter: "winter",
	Spring: "spring"
}

我们也可以通过使用数字作为值来做到这一点:

const Seasons = {
	Summer: 0,
	Autumn: 1,
	Winter: 2,
	Spring: 3
}

虽然这适用于代码量较少的情况,但我们将面临一些紧迫的问题:

1、你的代码很容易出错。开发人员可能会错误地使用定义范围之外的整数,或者在使用字符串时出现拼写错误。

const Seasons = {
    Summer: "summer",
    Autumn: "autumn",
    Winter: "winter",
    Spring: "spring"
}
const mySeason = "summr"

// 由于“mySeason”中的拼写错误,这将是返回 false
console.log(mySeason === Seasons.Summer)

2、来自不相关枚举的定义可能会重叠并导致冲突:

const Seasons = {
    Summer: 0,
    Autumn: 1,
    Winter: 2,
    Spring: 3
}

const Fruits = {
    Apple: 1,
    Orange: 1
}

// 理想情况下,这永远不应该为 true!
console.log(Seasons.Summer === Fruits.Apple)

3、使用原语(如字符串或数字)作为值在语义上是不正确的——季节不是真正的整数或字符串——它们是季节,应该有自己的类型,以便更好的语义表示。

带符号的枚举

符号(Symbol)让我们定义了保证不会相互冲突的值。

例如:

const Summer1 = Symbol("summer")
const Summer2 = Symbol("summer")

// 即使它们具有相同的表面值
// Summer1 和 Summer2 也不相等
console.log(Summer1 === Summer2)
// false

console.log(Summer1)

我们可以使用 Symbols 定义我们的枚举以确保它们不重复:

const Seasons = {
	Summer: Symbol("summer"),
	Autumn: Symbol("autumn"),
	Winter: Symbol("winter"),
	Spring: Symbol("spring")
}

let season = Seasons.Spring

switch (season) {
	case Seasons.Summer:
	console.log('the season is summer')
	break;
	case Seasons.Winter:
	console.log('the season is winter')
	break;
	case Seasons.Spring:
	console.log('the season is spring')
	break;
	case Seasons.Autumn:
	console.log('the season is autumn')
	break;
	default:
		console.log('season not defined')
}

使用 Symbols 确保我们分配的枚举值是使用我们最初定义的值。

**请注意,Symbols 无法序列化为 JSON,因此如果您计划将包含此枚举的对象转换为JSON字符串,则应该考虑使用前面的对象键的方法。

使Enum对象不可变

在前面的例子中,可以通过修改enum对象来更改枚举的值。例如,我们可以像这样更改Seasons对象:

const Seasons = {
	Summer: Symbol("summer"),
	Autumn: Symbol("autumn"),
	Winter: Symbol("winter"),
	Spring: Symbol("spring")
}

Seasons.Winter = "winter" // 这将用字符串覆盖 `Symbol`

对于代码量巨大的情况,很可能会无意中引入此类覆盖的错误。我们可以使用 Object.freeze 来防止这些变化:

const Seasons = Object.freeze({
	Summer: Symbol("summer"),
	Autumn: Symbol("autumn"),
	Winter: Symbol("winter"),
	Spring: Symbol("spring")
})

Seasons.Winter = "winter" // 这不会更改“Seasons”对象,因为它已被冻结

类枚举

为了使我们的代码在语义上更加正确,我们可以创建一个类来保存一组枚举。

例如,我们的季节应该有一种方法来识别它们都属于类似的分类。

让我们看看如何使用类和对象来创建不同的枚举组:

// Season 枚举可以作为一个类的静态成员分组
class Season {
  // 创建与静态属性相同的类的新实例
  static Summer = new Season("summer")
  static Autumn = new Season("autumn")
  static Winter = new Season("winter")
  static Spring = new Season("spring")

  constructor(name) {
    this.name = name
  }
}

// 现在我们可以使用命名空间赋值来访问枚举
// 在语义上清楚地表明"Summer"是"Season"
let season = Season.Summer

// 我们可以验证一个特定的变量是否是一个Season enum
console.log(season instanceof Season)
// true
console.log(Symbol('something') instanceof Season)
// false

// 我们可以根据每个enum类显式检查类型
console.log(season.constructor.name)
// 'Season'

列出所有可能的枚举值

如果我们使用上面基于类的实现方式,我们可以循环遍历 Season 类的键来获取同一组下的所有枚举值:

Object.keys(Season).forEach(season => console.log("season:", season))
// season: Summer
// season: Autumn
// season: Winter
// season: Spring

我们也可以对基于对象的方法做同样的事情:

const Seasons = Object.freeze({
	Summer: Symbol("summer"),
	Autumn: Symbol("autumn"),
	Winter: Symbol("winter"),
	Spring: Symbol("spring")
})

Object.keys(Seasons).forEach(season => console.log("season:", season))
// season: Summer
// season: Autumn
// season: Winter
// season: Spring

什么时候在 Javascript 中使用枚举?

一般来说,如果任何一个变量都有一定数量的固定值,枚举是有用的。

例如,Node.js 的加密标准库有一个支持算法的列表,可以将其视为一个枚举组。

在 Javascript 中正确使用枚举将会让你的代码更好、更稳定、更易于阅读且更不容易出错。


也可以看看


全国大流量卡免费领

19元月租ㆍ超值优惠ㆍ长期套餐ㆍ免费包邮ㆍ官方正品