ES6只读枚举可以将值映射到名称 [英] ES6 read-only enums that can map value to name
问题描述
我想在JS中定义类似枚举的结构,但有两个要求:
I would like to define an enum-like structure in JS, but have two requirements:
- 这些值是只读的,即,没有用户可以分配给他们。
- 可以将值(0,1,2,...)映射回名称(如Java的名称方法)
- The values be read-only, i.e. no users can assign to them.
- The values (0, 1, 2, ...) can be mapped back into the names (as with Java's name method)
我知道创建这样的枚举的方法通常符合一个要求,而不是两者。
The methods I know to create enums like this typically meet one requirement or the other, not both.
我试过:
const MyEnum = {
a: 0,
b: 1,
c: 2
};
枚举本身是常量,但值仍然是可变的,我无法将值映射回
The enum itself is constant, but the values are still mutable and I can't map values back to names efficiently.
在Typescript中编写枚举
时,它会输出:
When writing an enum
in Typescript, it outputs:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["a"] = 0] = "a";
MyEnum[MyEnum["b"] = 1] = "b";
MyEnum[MyEnum["c"] = 2] = "c";
})(MyEnum || (MyEnum = {}));
这可以映射两种方式,但仍然没有固定值。
This can map both ways, but still doesn't have constant values.
我发现符合这两个要求的唯一选项是在类上使用getter:
The only option I've found that meets both requirements would be using getters on a class:
class MyEnum {
get a() {
return 0;
}
...
}
此方法极大地限制合法的名称,并且有很多的开销,特别是在浏览器不能很好地嵌入getter(或不能)。
This method dramatically restricts the legal names and has a lot of overhead, especially in browsers that don't inline getters well (or can't).
const MyEnum = Object.freeze({
a: 0,
b: 1,
c: 2
});
这满足了常规的要求,但并没有提供将值映射到名称的好方法
This meets the constant requirement well, but doesn't provide a great way to map values back to names.
我可以编写一个帮助器来构建反向映射,如:
I could write a helper that builds the reverse mapping like:
function reverseEnum(enum) {
Object.keys(enum).forEach(k => {
enum[enum[k]] = k;
});
}
但是,生成反向映射的任何类型的编程解决方案将遇到问题,如果原始对象被冻结或以其他方式实际上是不变的。
But any kind of programmatic solution to generate the reverse mapping will run into problems if the original object is frozen or otherwise actually constant.
JS中有没有一个干净简洁的解决方案?
Is there a clean, concise solution to this in JS?
推荐答案
我将使用地图,以便您的枚举值可以是任何类型,而不是将其强制为字符串。
I'd use a Map so that your enum values can be any type, rather than having them coerced into strings.
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);
}
如果您的枚举是字符串,我也可能会更改一行:
If your enum is all strings, I'd also probably change one line to:
EnumLookup[key] = Symbol(obj[key]);
以确保枚举值正确使用。只使用一个字符串,你不能保证一些代码没有简单地传递一个正常的字符串,恰好与一个枚举值相同。如果您的值始终是字符串或符号,您还可以将Map简单地替换为简单对象。
to ensure that the enum values are being used properly. Using just a string, you have no guarantee that some code hasn't simply passed a normal string that happens to be the same as one of your enum values. If your values are always strings or symbols, you could also swap out the Map for a simple object.
这篇关于ES6只读枚举可以将值映射到名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!