JavaScript创建枚举

 相比直接写数字与字符串值,用枚举表示预定义范围的常量值有很多优点,这里就不做赘述了,但目前为止javascript并没有提供原生的enum类型(typescript当然就支持),通常javascript会借助对象类型来等效实现enum类型,实现方式有多种,这里介绍基于代理的枚举实现方式:

(其它实现方式及优缺点详见: https://zhuanlan.zhihu.com/p/629573201在JavaScript中4种创建枚举方式)

基于代理的枚举

代理枚举是一种有趣的实现方式。一个代理对象是一个特殊的对象,它包装另一个对象并修改对原始对象的操作的行为。代理不会改变原始对象的结构。

枚举代理拦截枚举对象的读取和写入操作,并且:

  • 当访问不存在的枚举值时抛出错误
  • 当更改枚举对象属性时抛出错误

代理的get()方法拦截读操作,并在属性名称不存在时抛出错误。set()方法拦截写操作并抛出错误。它旨在保护枚举对象免受写操作的影响。

//enum.js:

//说明:早期的浏览器或某些IE版本可能不支持Proxy(虽然IE已退出舞台,但考虑一些还需要IE的旧项目场景), 这里尽量做下兼容实现:

(function (global) {
    if (!!Proxy) {
        global.Enum = function(baseEnum) {
            return new Proxy(baseEnum, {
                get(target, name) {
                    if (!baseEnum.hasOwnProperty(name) && !name.toString() === 'Symbol(Symbol.toStringTag)') {
                        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')
                }
            })
        };        
    } else {
        global.Enum = function(baseEnum) {
            //return baseEnum;

            var newEnum = Object.assign({}, baseEnum);            
            Object.defineProperties(newEnum
                , Object.assign({}
                    , ...Object.keys(newEnum).map(function (key) {
                        return {
                            [key]: {
                                get: function () {
                                    //if (!newEnum.hasOwnProperty(key) && !key.toString() === 'Symbol(Symbol.toStringTag)') {
                                    //    throw new Error(`"${key}" value does not exist in the enum`)
                                    //}
                                    return baseEnum[key]    //注意这里不可newEnum[key]否则会死循环
                                },
                                set: function(value) {
                                    throw new Error('Cannot update the value of the enum')
                                }
                            }
                        }
                    })
                )
            );
            
            //防止新属性被添加到对象中(即防止该对象被扩展),只有当"use strict"时才会报错“Error: Cannot add property XXX, object is not extensible”, 否则只是添加新属性无效但不会报错:
            Object.preventExtensions(newEnum);

            return newEnum;
        };
    }
})(window)

 使用方式:

import { Enum } from './enum'
const Sizes = Enum({
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
})
const mySize = Sizes.Medium
console.log(mySize === Sizes.Medium) // logs true

代理枚举的使用方式和普通对象枚举完全相同

代理枚举的缺点

代理枚举的缺点是始终需要导入 Enum 工厂函数并将枚举对象包装在其中。与使用其他实现方式相比,代理枚举可能会带来一些性能损失。代理枚举涉及使用 JavaScript 的代理特性,这可能会使枚举对象的访问速度稍慢一丢丢。

参考资料:

在JavaScript中4种创建枚举方式 - 知乎

你可能感兴趣的:(Javascript,enum,枚举,js,enum)