目录
字符串和数字具有无数个值,而其他类型如布尔值则是有限的集合。
一周的日子(星期一,星期二,...,星期日),一年的季节(冬季,春季,夏季,秋季)和基本方向(北,东,南,西)都是具有有限值集合的例子。
当一个变量有一个来自有限的预定义常量的值时,使用枚举是很方便的。枚举使你不必使用魔法数字和字符串(这被认为是一种反模式)。
让我们看看在javascript中创建枚举的四种好方法(及其优缺点)。
基于对象的枚举
枚举是一种数据结构,它定义了一个有限的具名常量集。每个常量都可以通过其名称来访问。
让我们来考虑一件t恤衫的尺寸:small
,medium
,和large
。
在javascript中创建枚举的一个简单方法(虽然不是最理想的)是使用一个普通的javascript对象。
const sizes = { small: 'small', medium: 'medium', large: 'large', } const mysize = sizes.medium console.log(mysize === sizes.medium) // logs true
sizes
是一个基于javascript对象的枚举,它有三个具名常量:sizes.small
、sizes.medium
以及sizes.large
。
sizes
也是一个字符串枚举,因为具名常量的值是字符串:'small'
,'medium'
,以及 'large'
。
要访问具名常量值,请使用属性访问器。例如,sizes.medium
的值是'medium'
。
枚举的可读性更强,更明确,并消除了对魔法字符串或数字的使用。
优缺点
普通的对象枚举之所以吸引人,是因为它很简单:只要定义一个带有键和值的对象,枚举就可以了。
但是在一个大的代码库中,有人可能会意外地修改枚举对象,这将影响应用程序的运行。
const sizes = { small: 'small', medium: 'medium', large: 'large', } const size1 = sizes.medium const size2 = sizes.medium = 'foo' // changed! console.log(size1 === sizes.medium) // logs false
sizes.medium
枚举值被意外地改变。
size1
,虽然被初始化为sizes.medium
,但不再等同于sizes.medium
!
普通对象的实现没有受到保护,因此无法避免这种意外的改变。
让我们仔细看看字符串和symbol
枚举。以及如何冻结枚举对象以避免意外改变的问题。
枚举值类型
除了字符串类型,枚举值可以是一个数字:
const sizes = { small: 0, medium: 1, large: 2 } const mysize = sizes.medium console.log(mysize === sizes.medium) // logs true
上述例子中,sizes
枚举是数值枚举,因为值都是数字:0,1,2。
你也可以创建symbol
枚举:
const sizes = { small: symbol('small'), medium: symbol('medium'), large: symbol('large') } const mysize = sizes.medium console.log(mysize === sizes.medium) // logs true
使用symbol
的好处是,每个symbol
都是唯一的。这意味着,你总是要通过使用枚举本身来比较枚举:
const sizes = { small: symbol('small'), medium: symbol('medium'), large: symbol('large') } const mysize = sizes.medium console.log(mysize === sizes.medium) // logs true console.log(mysize === symbol('medium')) // logs false
使用symbol
枚举的缺点是json.stringify()
将symbol
字符串化为null
、undefined
,或者跳过有symbol
作为值的属性:
const sizes = { small: symbol('small'), medium: symbol('medium'), large: symbol('large') } const str1 = json.stringify(sizes.small) console.log(str1) // logs undefined const str2 = json.stringify([sizes.small]) console.log(str2) // logs '[null]' const str3 = json.stringify({ size: sizes.small }) console.log(str3) // logs '{}'
在下面的例子中,我将使用字符串枚举。但是你可以自由地使用你需要的任何值类型。
如果你可以自由选择枚举值类型,就用字符串吧。字符串比数字和symbol
更容易进行调试。
基于object.freeze()枚举
保护枚举对象不被修改的一个好方法是冻结它。当一个对象被冻结时,你不能修改或向该对象添加新的属性。换句话说,这个对象变成了只读。
在javascript中,object.freeze()
工具函数可以冻结一个对象。让我们来冻结sizes
枚举:
const sizes = object.freeze({ small: 'small', medium: 'medium', large: 'large', }) const mysize = sizes.medium console.log(mysize === sizes.medium) // logs true
const sizes = object.freeze({ ... })
创建一个冻结的对象。即使被冻结,你也可以自由地访问枚举值: const mysize = sizes.medium
。
优缺点
如果一个枚举属性被意外地改变了,javascript会抛出一个错误(在严格模式下):
const sizes = object.freeze({ small: 'small', medium: 'medium', large: 'large', }) const size1 = sizes.medium const size2 = sizes.medium = 'foo' // throws typeerror
语句const size2 = sizes.medium = 'foo'
对 sizes.medium
属性进行了意外的赋值。
因为sizes
是一个冻结的对象,javascript(在严格模式下)会抛出错误:
typeerror: cannot assign to read only property 'medium' of object