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

ssu*_*ube 17 enums ecmascript-6

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

  1. 值是只读的,即没有用户可以分配给它们.
  2. 值(0,1,2,...)可以映射回名称(与Java的名称方法一样)

我知道创建这样的枚举的方法通常满足一个要求或另一个要求,而不是两者.

我试过了:

const MyEnum = {
  a: 0,
  b: 1,
  c: 2
};
Run Code Online (Sandbox Code Playgroud)

枚举本身是常量,但值仍然是可变的,我无法有效地将值映射回名称.

在撰写一个enum打字稿时,它会输出:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["a"] = 0] = "a";
    MyEnum[MyEnum["b"] = 1] = "b";
    MyEnum[MyEnum["c"] = 2] = "c";
})(MyEnum || (MyEnum = {}));
Run Code Online (Sandbox Code Playgroud)

这可以映射两种方式,但仍然没有常量值.

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

class MyEnum {
  get a() {
    return 0;
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

这种方法极大地限制了合法名称并且有很多开销,特别是在没有内联(或不能)内联getter的浏览器中.

@Shmiddty建议冻结一个物体:

const MyEnum = Object.freeze({
  a: 0,
  b: 1,
  c: 2
});
Run Code Online (Sandbox Code Playgroud)

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

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

function reverseEnum(enum) {
  Object.keys(enum).forEach(k => {
    enum[enum[k]] = k;
  });
}
Run Code Online (Sandbox Code Playgroud)

但是,如果原始对象被冻结或者实际上是不变的,那么生成反向映射的任何类型的编程解决方案都会遇到问题.

在JS中有一个干净,简洁的解决方案吗?

Shm*_*dty 18

这个做得很好,恕我直言.

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);
}
Run Code Online (Sandbox Code Playgroud)


log*_*yth 10

我使用Map,以便你的枚举值可以是任何类型,而不是让它们强制成字符串.

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);
}
Run Code Online (Sandbox Code Playgroud)

如果你的枚举是所有字符串,我也可能将一行更改为:

EnumLookup[key] = Symbol(obj[key]);
Run Code Online (Sandbox Code Playgroud)

确保正确使用枚举值.仅使用一个字符串,您无法保证某些代码不会简单地传递一个恰好与您的一个枚举值相同的普通字符串.如果您的值始终是字符串或符号,则还可以将Map替换为简单对象.