ES6 перечисления только для чтения, которые могут сопоставлять значение с именем
Я хотел бы определить перечислительную структуру в JS, но у меня есть два требования:
- значения доступны только для чтения, то есть пользователи не могут их присваивать.
- значения (0, 1, 2,...) могут быть сопоставлены обратно в имена (как с Java name method )
Я пробовал:
const MyEnum = {
a: 0,
b: 1,
c: 2
};
Перечисление само по себе постоянно, но значения все еще изменчивы и я не могу эффективно сопоставлять значения с именами.
При написании enum
в машинописном виде он выводит:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["a"] = 0] = "a";
MyEnum[MyEnum["b"] = 1] = "b";
MyEnum[MyEnum["c"] = 2] = "c";
})(MyEnum || (MyEnum = {}));
Это может отображать оба пути, но все еще не имеет постоянных значений.
Единственный вариант, который я нашел, удовлетворяющий обоим требованиям, - это использование геттеров для класса:
class MyEnum {
get a() {
return 0;
}
...
}
Этот метод резко ограничивает легальные имена и имеет много накладных расходов, особенно в браузерах, которые не очень хорошо (или не могут) встроить геттеры.
@- предположил Шмидти. замораживание объекта :
const MyEnum = Object.freeze({
a: 0,
b: 1,
c: 2
});
Это хорошо соответствует постоянному требованию, но не обеспечивает отличный способ отображения значений обратно в имена.
Я мог бы написать помощник, который строит обратное отображение, например:
function reverseEnum(enum) {
Object.keys(enum).forEach(k => {
enum[enum[k]] = k;
});
}
Но любой вид программного решения для генерации обратного отображения столкнется с проблемами, если исходный объект заморожен или иным образом фактически постоянен.
Есть ли чистое, краткое решение этого в JS?
4 ответа:
Я бы использовал карту, чтобы ваши значения перечисления могли быть любого типа, а не принуждать их к строкам.
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]);
Для обеспечения правильного использования значений перечисления. Используя только строку, вы не можете гарантировать, что какой-то код просто не передал обычную строку, которая случайно совпадает с одним из ваших значений перечисления. Если ваши значения всегда являются строками или символами, вы также можете поменять карту местами для простого объекта.
Это делает довольно хорошую работу, ИМХО.
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); }
Совсем недавно реализована версия 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-красиво и удобно.
Использование: сначала импортируйте класс enum...
import {ErrorCode} from "../common/services/errors/ErrorCode";
Теперь, после импорта класса enum, откройте его следующим образом:
if( errCode.includes(ErrorCode.BAD_REQUEST.apiCode) ){...}
PS > Это используется в сочетании с установкой Webpack с использованием Babel для преобразуйте наши классы ES6 вниз для совместимости с браузером.
То же самое, что и у Shmiddty, но немного более многоразовое благодаря Ramda, и немного красивее благодаря coffeescript
R = require('ramda') lib = {} lib.reduceKeys = (obj) -> R.reduce R.__, R.__, R.keys obj lib.swapKeys = R.curry (obj, acc) -> (lib.reduceKeys obj) ((acc, key) -> acc[obj[key]] = key acc ), acc lib.copy = R.curry (obj, acc) -> (lib.reduceKeys obj) ((acc, key) -> acc[key] = obj[key] acc ), acc lib.Enum = (obj) -> swapped = lib.swapKeys obj, {} Object.freeze lib.copy obj, (val) -> swapped[val] exports[key] = val for own key, val of lib