Enums ES6只读枚举,可将值映射到名称
我想在JS中定义类似枚举的结构,但有两个要求: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) {
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) ){...}