优缺点如果一个枚举属性被意外地改变了,JavaScript会抛出一个错误(在严格模式下):
const Sizes = Object.freeze({Small: 'Small',Medium: 'Medium',Large: 'Large',})const size1 = Sizes.Mediumconst 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 <Object>
冻结的对象枚举被保护起来,不会被意外地改变 。
不过,还有一个问题 。如果你不小心把枚举常量拼错了,那么结果将是未undefined:
const Sizes = Object.freeze({Small: 'small',Medium: 'medium',Large: 'large',})console.log(Sizes.Med1um) // logs undefined
Sizes.Med1um表达式(Med1um是Medium的错误拼写版本)结果为未定义,而不是抛出一个关于不存在的枚举常量的错误 。
让我们看看基于代理的枚举如何解决这个问题 。
基于proxy枚举一个有趣的,也是我最喜欢的实现,是基于代理的枚举 。
代理是一个特殊的对象,它包裹着一个对象,以修改对原始对象的操作行为 。代理并不改变原始对象的结构 。
枚举代理拦截对枚举对象的读和写操作,并且:
- 当访问一个不存在的枚举值时,会抛出一个错误 。
- 当一个枚举对象的属性被改变时抛出一个错误
// enum.jsexport function Enum(baseEnum) {return new Proxy(baseEnum, {get(target, name) {if (!baseEnum.hasOwnProperty(name)) {throw new Error(`"${name}" value does not exist in the enum`)}return baseEnum[name]},set(target, name, value) {throw new Error('Cannot add a new value to the enum')}})}
代理的get()方法拦截读取操作,如果属性名称不存在,则抛出一个错误 。set()方法拦截写操作,但只是抛出一个错误 。这是为保护枚举对象不被写入操作而设计的 。
让我们把sizes对象枚举包装成一个代理:
import { Enum } from './enum'const Sizes = Enum({Small: 'small',Medium: 'medium',Large: 'large',})const mySize = Sizes.Mediumconsole.log(mySize === Sizes.Medium) // logs true
代理枚举的工作方式与普通对象枚举完全一样 。优缺点然而,代理枚举受到保护,以防止意外覆盖或访问不存在的枚举常量:
import { Enum } from './enum'const Sizes = Enum({Small: 'small',Medium: 'medium',Large: 'large',})const size1 = Sizes.Med1um// throws Error: non-existing constantconst size2 = Sizes.Medium = 'foo' // throws Error: changing the enum
Sizes.Med1um抛出一个错误,因为Med1um常量名称在枚举中不存在 。Sizes.Medium = 'foo' 抛出一个错误,因为枚举属性已被改变 。
代理枚举的缺点是,你总是要导入枚举工厂函数,并将你的枚举对象包裹在其中 。
基于类的枚举另一种有趣的创建枚举的方法是使用一个JavaScript类 。
一个基于类的枚举包含一组静态字段,其中每个静态字段代表一个枚举的常量 。每个枚举常量的值本身就是该类的一个实例 。
让我们用一个Sizes类来实现sizes枚举:
class Sizes {static Small = new Sizes('small')static Medium = new Sizes('medium')static Large = new Sizes('large')#valueconstructor(value) {this.#value = https://www.isolves.com/it/cxkf/yy/js/2023-05-22/value}toString() {return this.#value}}const mySize = Sizes.Smallconsole.log(mySize === Sizes.Small)// logs trueconsole.log(mySize instanceof Sizes) // logs true
Sizes是一个代表枚举的类 。枚举常量是该类的静态字段,例如,static Small = new Sizes('small') 。Sizes类的每个实例也有一个私有字段#value,它代表枚举的原始值 。
基于类的枚举的一个很好的优点是能够在运行时使用instanceof操作来确定值是否是枚举 。例如,mySize instanceof Sizes结果为真,因为mySize是一个枚举值 。
基于类的枚举比较是基于实例的(而不是在普通、冻结或代理枚举的情况下的原始比较):
class Sizes {static Small = new Sizes('small')static Medium = new Sizes('medium')static Large = new Sizes('large')#valueconstructor(value) {this.#value = https://www.isolves.com/it/cxkf/yy/js/2023-05-22/value}toString() {return this.#value}}const mySize = Sizes.Smallconsole.log(mySize === new Sizes('small')) // logs false
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Maps与WeakMaps在DOM节点管理中的妙用
- JavaScript检测用户是否在线!
- JavaScript 的 Anti-Debugging 技術
- 这四种右转弯,很容易违章被罚款记分,看一次就能记住不犯错误!
- 杜阿·利帕|《平凡之路》压轴出场的她,才是剧中的“颜值天花板”
- 博物馆|古钱币中的祈福钱,乾道嘉
- 洗面奶|2023我心目中的洗面奶排行榜
- 钱币|10枚宋代古钱中的王者
- 如何使用Python中的OCR技术将图像中的文本提取为可编辑文件?
- 海南黄花梨|海南黄花梨木头中的皇后,真的是名不虚传