Enums ES6只读枚举,可将值映射到名称

Enums ES6只读枚举,可将值映射到名称,enums,ecmascript-6,Enums,Ecmascript 6,我想在JS中定义类似枚举的结构,但有两个要求: 这些值是只读的,即没有用户可以分配给它们 值(0,1,2,…)可以映射回名称(与Java的一样) 我所知道的创建这样的枚举的方法通常满足一个或另一个需求,而不是同时满足两个需求 我试过: const MyEnum = { a: 0, b: 1, c: 2 }; 枚举本身是常量,但值仍然是可变的,我无法有效地将值映射回名称 在Typescript中写入enum时,它输出: var MyEnum; (function (MyEnum) {

我想在JS中定义类似枚举的结构,但有两个要求:

  • 这些值是只读的,即没有用户可以分配给它们
  • 值(0,1,2,…)可以映射回名称(与Java的一样)
  • 我所知道的创建这样的枚举的方法通常满足一个或另一个需求,而不是同时满足两个需求

    我试过:

    const MyEnum = {
      a: 0,
      b: 1,
      c: 2
    };
    
    枚举本身是常量,但值仍然是可变的,我无法有效地将值映射回名称

    在Typescript中写入
    enum
    时,它输出:

    var MyEnum;
    (function (MyEnum) {
        MyEnum[MyEnum["a"] = 0] = "a";
        MyEnum[MyEnum["b"] = 1] = "b";
        MyEnum[MyEnum["c"] = 2] = "c";
    })(MyEnum || (MyEnum = {}));
    
    这可以双向映射,但仍然没有常量值

    我发现满足这两个要求的唯一选择是在类上使用getter:

    class MyEnum {
      get a() {
        return 0;
      }
      ...
    }
    
    这种方法极大地限制了合法名称,并且有很多开销,特别是在没有很好地内联getter(或者不能内联getter)的浏览器中

    :

    这很好地满足了常量要求,但并不能提供将值映射回名称的好方法

    我可以编写一个帮助器来构建反向映射,如下所示:

    function reverseEnum(enum) {
      Object.keys(enum).forEach(k => {
        enum[enum[k]] = k;
      });
    }
    
    但是,如果原始对象被冻结或实际上是常量,任何生成反向映射的编程解决方案都会遇到问题

    在JS中是否有一个简洁明了的解决方案?

    做得很好,IMHO

    function Enum(a){
      let i = Object
        .keys(a)
        .reduce((o,k)=>(o[a[k]]=k,o),{});
    
      return Object.freeze(
        Object.keys(a).reduce(
          (o,k)=>(o[k]=a[k],o), v=>i[v]
        )
      );
    } // y u so terse?
    
    const FOO = Enum({
      a: 0,
      b: 1,
      c: "banana"
    });
    
    console.log(FOO.a, FOO.b, FOO.c);            // 0 1 banana
    console.log(FOO(0), FOO(1), FOO("banana"));  // a b c
    
    try {
      FOO.a = "nope";
    }
    catch (e){
      console.log(e);
    }
    

    我会使用一个映射,这样您的枚举值就可以是任何类型,而不是将它们强制为字符串

    function Enum(obj){
        const keysByValue = new Map();
        const EnumLookup = value => keysByValue.get(value);
    
        for (const key of Object.keys(obj)){
            EnumLookup[key] = obj[key];
            keysByValue.set(EnumLookup[key], key);
        }
    
        // Return a function with all your enum properties attached.
        // Calling the function with the value will return the key.
        return Object.freeze(EnumLookup);
    }
    
    如果您的枚举都是字符串,我还可能将其中一行更改为:

    EnumLookup[key] = Symbol(obj[key]);
    

    以确保正确使用枚举值。仅使用一个字符串,无法保证某些代码没有简单地传递一个与枚举值相同的普通字符串。如果您的值始终是字符串或符号,您也可以将地图换成简单的对象。

    最近刚刚实现了一个Es6版本,该版本运行良好:

    const k_VALUES = {}
    
    export class ErrorCode {
    
        constructor(p_apiCode, p_httpCode){
            this.apiCode = p_apiCode;
            this.httpCode = p_httpCode;
    
            k_VALUES[p_apiCode] = this;
        }
    
    
        static create(p_apiCode){
            if(k_VALUES[p_apiCode]){
                return k_VALUES[p_apiCode];
            }
    
            return ErrorCode.UNKNOWN;
        }
    }
    
    ErrorCode.UNKNOWN                 = new ErrorCode(0,     500);
    ErrorCode.NOT_FOUND               = new ErrorCode(-1000, 404);
    ErrorCode.NOT_FOUND_EMAIL         = new ErrorCode(-1001, 404);
    ErrorCode.BAD_REQUEST             = new ErrorCode(-1010, 404);
    
    我想实现一个与Java枚举类似的模式。这使我能够使用构造函数传递值。然后,构造函数冻结ErrorCode对象-非常方便

    用法:首先导入枚举类

    import {ErrorCode} from "../common/services/errors/ErrorCode";
    
    现在,在导入enum类后,按如下方式访问它:

    if( errCode.includes(ErrorCode.BAD_REQUEST.apiCode) ){...}
    

    PS>这与使用Babel的网页包设置一起使用,以转换我们的ES6类以实现浏览器兼容性。

    为什么不创建枚举,添加反向属性,然后将其冻结?尽管如此,我还是很高兴能简明扼要地做到这一点。@apsillers,这是个不错的主意。定义一个普通常量枚举对象,然后运行一个帮助器来设置反向映射并冻结。我可以很容易地维持一个电话,所以这将非常有效。要将其转换为答案吗?是否还需要通过的特定值?我可能也会用
    Symbol
    来包装所有的值,但我不知道您期望的是什么。通常情况下,枚举值实际上并不重要,至少在我编写的代码中是这样。
    const MyEnum=Object.freeze(reverseEnum({…}))
    似乎工作得很好,不是吗?你可以在构造函数末尾冻结它。
    //TODO:write es5等价物
    我已经使用Ramda库将它移植到下面的coffescript中,它应该编译成es5友好的JS,同时对匿名函数使用漂亮的箭头语法,就像这样,它似乎与Babel的polyfill for Map配合得很好。“如果[me]enum是所有字符串?”是什么意思?键还是值?
    Symbol
    创建一个新的唯一值,因此只有在您不关心值是什么的情况下才有用。我只提到字符串,因为您要将字符串传递给符号('something')使调试更容易。许多语言都将枚举值视为不透明的值,如果您在JS中使用类似的枚举,那么使用符号就更好了。
    if( errCode.includes(ErrorCode.BAD_REQUEST.apiCode) ){...}