Какой синтаксис является предпочтительным для определения перечислений в JavaScript?


2082

Какой синтаксис является предпочтительным для определения перечислений в JavaScript? Что-то вроде:

my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
   // whatever
}

Или есть более предпочтительная идиома?


132
Не используйте 0в качестве номера перечисления. Если это не используется для чего-то, что не было установлено. JS рассматривает false || undefined || null || 0 || "" || '' || NaNвсе как одно и то же значение при сравнении с использованием ==.
Мацко

153
@matsko это не просто аргумент против использования ==?
sdm350

6
0 == nullвозвращает false
mcont

11
Но false == 0и +null == 0(и преобразования в числа происходят иногда, когда вы этого не ожидаете), а null == undefinedтакже и +undefinedесть NaN(хотя NaN != NaN).
sanderd17

46
Матрица двойного равенства более запутана, чем автоматическое форматирование в
Microsoft Word

Ответы:


896

Начиная с версии 1.8.5 можно запечатать и заморозить объект, поэтому определите вышеизложенное как:

const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})

или

const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)

и вуаля! JS перечисления.

Тем не менее, это не мешает вам присвоить нежелательное значение переменной, что часто является главной целью перечислений:

let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors

Один из способов обеспечить более высокую степень безопасности типов (с помощью перечислений или иным образом) - использовать такой инструмент, как TypeScript или Flow .

Источник

Цитаты не нужны, но я сохранил их для согласованности.


6
Согласно Википедии ( en.wikipedia.org/wiki/JavaScript#Versions ) это применимо к Firefox 4, IE 9, Opera 11.60, и я знаю, что это работает в Chrome.
Артур Цайка

77
Это правильный ответ сейчас в 2012 году более простой: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Вам не нужно указывать идентификатор, вы можете просто использовать пустой объект для сравнения перечислений. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Габриэль Ламас

34
Для обратной совместимости,if (Object.freeze) { Object.freeze(DaysEnum); }
приветствуем

17
Я хотел бы отметить, что выполнение ({ monday: {}, и т. Д. Означает, что если вы преобразуете этот объект в JSON с помощью stringify, вы получите, [{"day": {}}]что не сработает.
13

10
@ Supuhstar Мое мнение по этому вопросу сейчас другое. Не используйте freeze (), это совершенно бесполезно и тратит время на «глупые» вещи. Если вы хотите , чтобы выставить перечисление, просто выставить это: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Сравнение объектов, как в моем предыдущем комментарии, НАМНОГО БОЛЬШЕ, чем сравнение чисел.
Габриэль Ламас

608

Это не очень хороший ответ, но я бы сказал, что он работает очень хорошо, лично

Тем не менее, поскольку значения не имеют значения (вы использовали 0, 1, 2), я бы использовал значимую строку на случай, если вы когда-нибудь захотите вывести текущее значение.


377
Об этом говорилось в другом ответе, но так как этот ответ является принятым ответом, я опубликую его здесь. Решение ОП является правильным. Хотя будет еще лучше, если использовать с Object.freeze(). Это не позволит другому коду изменять значения перечисления. Пример:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth

5
@TolgaE спасибо за эту библиотеку! Это вдохновило меня не только свести его к минимуму, но и добавить пару функций! Я раздвоил твой и выложил все это здесь: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar

3
@ Supuhstar Это здорово! Я рад, что вы можете использовать его. Не стесняйтесь делать запрос на включение, если вы хотите, чтобы он был объединен в этой библиотеке, тогда я могу обновить библиотеку npm
Tolga E

2
Если кому-то интересно, я реализовал безопасные по типу перечисления, аналогичные тем, которые есть в Java. Это означает, что вы можете делать instanceofпроверки. Например ColorEnum.RED instanceof ColorEnum(возвращает true). Вы также можете разрешить экземпляр из имени ColorEnum.fromName("RED") === ColorEnum.RED(возвращает true). Каждый экземпляр имеет также .name()и к .ordinal()способу, и само перечисление имеет values()метод , который returnd массив всех констант.
Вивин Палиат,

3
Я не уверен, что согласен с предложением "значимой строки". Перечисления не должны рассматриваться как строки или числа; это абстрактные типы данных. Не должно быть возможности «вывести текущее значение» без какого-либо вспомогательного метода. В Java и .NET это ToString()метод. Мы, разработчики JS, уже слишком зависимы от того, что «просто работает»! Также нужно уметь быстро switchперечислять. Сравнение строк выполняется медленнее, чем сравнение чисел, поэтому производительность будет немного хуже, switchесли вы будете использовать строки вместо целых чисел.
Rabadash8820

501

ОБНОВИТЬ

Спасибо всем за поддержку, но я не думаю, что мой ответ ниже - лучший способ написать перечисления в JavaScript. Смотрите мой блог для более подробной информации: Перечисления в JavaScript .


Оповещение имени уже возможно:

if (currentColor == my.namespace.ColorEnum.RED) {
   // alert name of currentColor (RED: 0)
   var col = my.namespace.ColorEnum;
   for (var name in col) {
     if (col[name] == col.RED)
       alert(name);
   }
}

В качестве альтернативы, вы можете создать объекты значений, чтобы вы могли получить торт и съесть его:

var SIZE = {
  SMALL : {value: 0, name: "Small", code: "S"}, 
  MEDIUM: {value: 1, name: "Medium", code: "M"}, 
  LARGE : {value: 2, name: "Large", code: "L"}
};

var currentSize = SIZE.MEDIUM;
if (currentSize == SIZE.MEDIUM) {
  // this alerts: "1: Medium"
  alert(currentSize.value + ": " + currentSize.name);
}

В JavaScript, поскольку это динамический язык, можно даже добавить значения enum в набор позже:

// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};

Помните, что поля перечисления (значение, имя и код в этом примере) не нужны для проверки идентичности и существуют только для удобства. Также имя свойства size не обязательно должно быть жестко закодировано, но также может быть установлено динамически. Итак, предположим, что вы знаете только имя для нового значения перечисления, вы все равно можете добавить его без проблем:

// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};

Конечно, это означает, что некоторые предположения больше не могут быть сделаны (например, это значение представляет правильный порядок для размера).

Помните, что в JavaScript объект похож на карту или хэш-таблицу . Набор пар имя-значение. Вы можете просматривать их или иным образом манипулировать ими, не зная о них заранее.

пример

for (var sz in SIZE) {
  // sz will be the names of the objects in SIZE, so
  // 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'
  var size = SIZE[sz]; // Get the object mapped to the name in sz
  for (var prop in size) {
    // Get all the properties of the size object, iterates over
    // 'value', 'name' and 'code'. You can inspect everything this way.        
  }
} 

И, кстати, если вы заинтересованы в пространствах имен, вы можете взглянуть на мое решение для простого, но мощного управления пространством имен и зависимостей для JavaScript: Пакеты JS


Итак, как бы вы пошли и создали просто РАЗМЕР, если у вас есть только его имя?
Йоханисма

2
@Johanisma: этот вариант использования не имеет смысла для перечислений, так как вся идея в том, что вы знаете все значения заранее. Однако ничто не мешает вам добавить дополнительные значения позже в Javascript. Я добавлю пример этого в мой ответ.
Стейн де Витт

2
+1 за ссылку на ваш пост с подходом свойств. Элегантный в том, что основные объявления просты, как в OP, с добавленной функцией свойств при желании.
goodeye,

@Stijin, вам очень понравилось ваше обновленное решение. Размещенный код в комментариях к вашему блогу и в качестве комментария ниже. По сути, используя функцию, выполните сборку свойств из существующего хеш-списка и при необходимости замораживайте его (mkenum_2 в моем списке). Приветствия.
Андрей Филипс

Есть также библиотека, которая реализует это, также включая хорошие функции для сравнения и обратного поиска: github.com/adrai/enum
Roman M

83

Итог: вы не можете.

Вы можете подделать это, но вы не получите безопасность типа. Обычно это делается путем создания простого словаря строковых значений, сопоставленных с целочисленными значениями. Например:

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}

Document.Write("Enumerant: " + DaysEnum.tuesday);

Проблема с этим подходом? Вы можете случайно переопределить свой перечислитель или случайно иметь повторяющиеся значения перечислителя. Например:

DaysEnum.monday = 4; // whoops, monday is now thursday, too

редактировать

А как насчет Object.freeze Артура Цайка? Разве это не сработает, чтобы помешать вам установить понедельник на четверг? - Фрай Квад

Абсолютно, Object.freezeполностью решит проблему, на которую я жаловался. Я хотел бы напомнить всем, что, когда я писал выше, на Object.freezeсамом деле не существовало.

Теперь .... теперь это открывает некоторые очень интересные возможности.

Edit 2
Вот очень хорошая библиотека для создания перечислений.

http://www.2ality.com/2011/10/enums.html

Хотя это, вероятно, не подходит для каждого допустимого использования перечислений, оно идет очень далеко.


103
есть безопасность типов в javascript?
Скотт Эверден

3
Поэтому не отображайте значения в свойствах объекта. Используйте getter для доступа к перечислителю (хранится как свойство, скажем, «частного» объекта). Наивная реализация выглядела бы как -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax

2
@ Скотт Эверден: точка взята. @kangax: дело в том, что это все еще хак. Перечисления просто не существуют в Javascript, период, конец истории. Даже шаблон, предложенный Тимом Сильвестром, все еще не идеальный взлом.
Рандольфо

2
Обсыпка кода литералами не очень удобна в обслуживании, поэтому имеет смысл создавать для него константы. Конечно, у Javascript также нет констант. Так что в основном это просто способ написания чистого кода. Это не может быть применено, но не так много в Javascript. Вы можете переопределить константы, или функции, или, в основном, что угодно. EG: document.getElementById = function () {alert ("Вы облажались. Javascript не безопасен для типов.");};
Стейн де Витт

3
@Randolpho: А как насчет Object.freeze Артура Цайка? Разве это не сработает, чтобы помешать вам установить понедельник на четверг?
Майкл - Где Клэй Ширки

56

Вот что мы все хотим:

function Enum(constantsList) {
    for (var i in constantsList) {
        this[constantsList[i]] = i;
    }
}

Теперь вы можете создавать свои перечисления:

var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);

Делая это, можно получить доступ к константам обычным способом (YesNo.YES, Color.GREEN) и получить последовательное значение типа int (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).

Вы также можете добавить методы, используя Enum.prototype:

Enum.prototype.values = function() {
    return this.allValues;
    /* for the above to work, you'd need to do
            this.allValues = constantsList at the constructor */
};


Редактировать - небольшое улучшение - теперь с varargs: (к сожалению, это не работает должным образом в IE: S ... тогда следует придерживаться предыдущей версии)

function Enum() {
    for (var i in arguments) {
        this[arguments[i]] = i;
    }
}

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');

Люблю простоту этого ответа!
Marquizzo

@Marquizzo (и OP) Я создал улучшенную версию на основе этого ответа: stackoverflow.com/a/60309416/1599699
Эндрю

53

В большинстве современных браузеров существует тип данных символьного примитива, который можно использовать для создания перечисления. Это обеспечит безопасность типа перечисления в каждое значение символа гарантируется JavaScript должен быть уникальным, то есть Symbol() != Symbol(). Например:

const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});

Чтобы упростить отладку, вы можете добавить описание к перечисляемым значениям:

const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});

Plunker demo

На GitHub вы можете найти оболочку, которая упрощает код, необходимый для инициализации enum:

const color = new Enum("RED", "BLUE")

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE

Это правильный ответ в теории. На практике поддержка браузеров в 2015 году далеко не достаточна. Не готово к производству.
vbraun

1
Хотя поддержки браузера пока нет, это лучший ответ, поскольку он близок к тому, для чего Symbolон предназначен.
rvighne

2
Значения meh ... enum часто должны быть сериализуемыми, хотя символы не так удобны для сериализации и десериализации.
Энди

3
Это только я или Object.freezeтолько для людей, которые не приняли тот факт, что «monkeypatch на свой страх и риск» является социальным контрактом JS?
Энди

@ И да, сериализация раздражает. Я закончил тем, что делал явное toJSONдля класса, который использует этот подход: stackoverflow.com/questions/58499828/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

30

𝗣𝗹𝗮𝗶𝗻 𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲 𝗡𝗮𝗺𝗲𝘀

Давайте обратимся прямо к проблеме: размер файла. Любой другой ответ, перечисленный здесь, увеличивает ваш код до крайности. Я представляю вам, что для наилучшей производительности, читабельности кода, управления крупномасштабными проектами, подсказок синтаксиса во многих редакторах кода и уменьшения размера кода путем минимизации, это правильный способ сделать перечисления: переменные подчеркивания.


wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwv

Переменные подчеркивания

Как показано в приведенной выше таблице и в примере ниже, для начала есть пять простых шагов:

  1. Определите имя для группы перечислений. Подумайте о существительном, которое может описать цель перечисления или, по крайней мере, записи в перечислении. Например, группа перечислений, представляющих цвета, выбираемые пользователем, может быть лучше названа COLORCHOICES, чем COLORS.
  2. Решите, являются ли перечисления в группе взаимоисключающими или независимыми. Если взаимоисключающие, начинайте каждое перечисляемое имя переменной с ENUM_. Если независимый или бок о бок, используйте INDEX_.
  3. Для каждой записи создайте новую локальную переменную, имя которой начинается с ENUM_или INDEX_, затем имя группы, затем подчеркивание, а затем уникальное понятное имя для свойства.
  4. Добавить ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_или INDEXLEN_(независимо от того LEN_илиLENGTH_ это личное предпочтение) перечисленное переменной в самом конце. Вы должны использовать эту переменную везде, где это возможно, в вашем коде, чтобы гарантировать, что добавление дополнительной записи в перечисление и увеличение этого значения не нарушит ваш код.
  5. Дайте каждый последующий Перенумерованные переменным значение одного больше , чем в прошлом, начиная с 0. Есть комментарии на этой странице, например , 0не следует использовать в качестве перечисленного значения , так как 0 == null, 0 == false, 0 == ""и другие JS сумасшествий. Я утверждаю, что, чтобы избежать этой проблемы и одновременно повысить производительность, всегда используйте ===и никогда не позволяйте ==появляться в вашем коде, кроме typeof(ex typeof X == "string"). За все годы использования ===у меня никогда не было проблем с использованием 0 в качестве значения перечисления. Если вы по-прежнему брезгливы, то 1можете использовать его как начальное значение в ENUM_перечислениях (но не в INDEX_перечислениях) без потери производительности во многих случаях.
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

Вот как я помню, когда использовать INDEX_и когда использовать ENUM_:

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

Однако ENUM_в определенных обстоятельствах может быть уместным в качестве индекса, например, при подсчете вхождений каждого элемента.

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

Заметьте, что в приведенном выше коде действительно легко добавить новый вид питомца: вам просто нужно добавить новую запись после ENUM_PET_RATи обновить ENUMLEN_PETсоответствующим образом. Может быть сложнее и глючно добавить новую запись в другие системы перечисления.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw

𝗘𝘅𝘁𝗲𝗻𝗱 𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀 𝗪𝗶𝘁𝗵 𝗔𝗱𝗱𝗶𝘁𝗶𝗼𝗻

Кроме того, этот синтаксис перечислений допускает ясное и краткое расширение классов, как показано ниже. Чтобы расширить класс, добавьте инкрементное число к LEN_записи родительского класса. Затем завершите подкласс своей собственной LEN_записью, чтобы подкласс мог быть расширен в будущем.

Диаграмма расширения дополнения

(function(window){
    "use strict";
    var parseInt = window.parseInt;

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

    // the red component of green
    console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]);
    // the alpha of transparent purple
    console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); 
    // the enumerated array for turquoise
    console.log(parseHexColor("#40E0D0"));
})(self);

(Длина: 2450 байт)

Некоторые могут сказать, что это менее практично, чем другие решения: оно занимает много места, требует много времени для написания и не содержит синтаксиса сахара. Эти люди были бы правы, если бы они не минимизировали свой код. Тем не менее, ни один разумный человек не оставит единый код в конечном продукте. Для этого миниатюры Closure Compiler - лучшее, что я пока не нашел. Онлайн доступ можно найти здесь . Компилятор Closure может взять все эти перечисленные данные и вставить их в строку, что делает ваш Javascript очень маленьким и быстро запускаемым. Таким образом, Minify with Closure Compiler. Обратите внимание.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw

𝗠𝗶𝗻𝗶𝗳𝘆 𝗪𝗶𝘁𝗵 𝗖𝗹𝗼𝘀𝘂𝗿𝗲 𝗖𝗼𝗺𝗽𝗶𝗹𝗲𝗿

Компилятор Closure способен выполнять некоторые довольно невероятные оптимизации с помощью умозаключений, которые намного превосходят возможности любого другого Javascript minifier. Закрытие Компилятор может встроить примитивные переменные с фиксированным значением. Закрывающий компилятор также может делать выводы на основе этих встроенных значений и исключать неиспользуемые блоки в операторах if и циклах.

Завершение кода с помощью Closure Compiler

'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);

(Длина: 605 байт)

Closure Compiler вознаграждает вас за правильное кодирование и хорошую организацию вашего кода, потому что, в то время как многие минифайеры наказывают организованный код большим уменьшенным размером файла, Closure Compiler может просеять всю вашу чистоту и разумность, чтобы вывести файл еще меньшего размера, если вы используете хитрости как перечисления имен переменных. В этом едином мнении есть святой Грааль кодирования: инструмент, который одновременно помогает вашему коду с меньшим уменьшенным размером и помогает вашему уму, обучая лучшим навыкам программирования.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw

𝗦𝗺𝗮𝗹𝗹𝗲𝗿 𝗖𝗼𝗱𝗲 𝗦𝗶𝘇𝗲

Теперь давайте посмотрим, насколько большим будет эквивалентный файл без каких-либо перечислений.

Исходный код без использования перечислений (длина: 1973 байта (на 477 байт короче перечисляемого кода!))
Сокращен без использования перечислений (длина: 843 байта (на 238 байт длиннее перечисляемого кода ))

Диаграмма размеров кода



Как видно, без перечислений исходный код короче за счет большего уменьшенного кода. Я не знаю о тебе; но я точно знаю, что не включаю исходный код в конечный продукт. Таким образом, эта форма перечислений намного лучше, так как она приводит к меньшим уменьшенным размерам файлов.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw

𝗖𝗼𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝘃𝗲 🤝 𝗕𝘂𝗴 𝗙𝗶𝘅𝗶𝗻𝗴

Еще одно преимущество этой формы перечисления состоит в том, что она может использоваться для простого управления крупномасштабными проектами без ущерба для минимизированного размера кода. При работе над большим проектом с большим количеством других людей может быть полезно явно пометить и пометить имена переменных, с которыми был создан код, чтобы можно было быстро определить первоначального создателя кода для совместного исправления ошибок.

// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED   = 0,
      ENUM_JG_COLORENUM_GREEN = 1,
      ENUM_JG_COLORENUM_BLUE  = 2,
      ENUMLEN_JG_COLORENUM    = 3;

// later on

if(currentColor === ENUM_JG_COLORENUM_RED) {
   // whatever
}

// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED   = 0,
      ENUM_PL_ARRAYTYPE_ISSORTED   = 1,
      ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob Knight
      ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
      ENUMLEN_PL_COLORENUM         = 4;

// later on

if(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

𝗦𝘂𝗽𝗲𝗿𝗶𝗼𝗿 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

Кроме того, эта форма перечисления также намного быстрее после минификации. В обычных именованных свойствах браузер должен использовать хеш-карты для поиска того, где находится свойство на объекте. Хотя JIT-компиляторы интеллектуально кэшируют это местоположение на объекте, все еще существуют огромные накладные расходы из-за особых случаев, таких как удаление более низкого свойства из объекта.

Но при использовании непрерывных неразреженных массивов PACKED_ELEMENTS с целочисленным индексом браузер может пропустить большую часть этих издержек, поскольку индекс значения во внутреннем массиве уже указан. Да, согласно стандарту ECMAScript все свойства должны рассматриваться как строки. Тем не менее, этот аспект стандарта ECMAScript очень вводит в заблуждение относительно производительности, поскольку все браузеры имеют специальные оптимизации для числовых индексов в массивах.

/// Hashmaps are slow, even with JIT juice
var ref = {};
ref.count = 10;
ref.value = "foobar";

Сравните код выше с кодом ниже.

/// Arrays, however, are always lightning fast
const INDEX_REFERENCE_COUNT = 0;
const INDEX_REFERENCE_VALUE = 1;
const INDEXLENGTH_REFERENCE = 2;

var ref = [];
ref[INDEX_REFERENCE_COUNT] = 10;
ref[INDEX_REFERENCE_VALUE] = "foobar";

Можно возразить против кода с перечислениями, которые кажутся намного длиннее, чем код с обычными объектами, но внешний вид может быть обманчивым. Важно помнить, что размер исходного кода не пропорционален размеру вывода при использовании epic Closure Compiler. Обратите внимание.

/// Hashmaps are slow, even with JIT juice
var a={count:10,value:"foobar"};

Минимизированный код без перечислений выше, а минимизированный код с перечислениями ниже.

/// Arrays, however, are always lightning fast
var a=[10,"foobar"];

Приведенный выше пример демонстрирует, что в дополнение к превосходной производительности перечисляемый код также приводит к уменьшению уменьшенного размера файла.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw

𝗘𝗮𝘀𝘆 𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

Кроме того, личная вишня сверху использует эту форму перечислений вместе с текстовым редактором CodeMirror в режиме Javascript. Режим подсветки синтаксиса Javascript в CodeMirror выделяет локальные переменные в текущей области видимости. Таким образом, вы сразу узнаете, когда правильно вводите имя переменной, потому что, если имя переменной было ранее объявлено с varключевым словом, тогда имя переменной приобретает особый цвет (по умолчанию голубой). Даже если вы не используете CodeMirror, то по крайней мере браузер выдает полезную[variable name] is not definedисключение при выполнении кода с ошибочными именами перечислений. Кроме того, инструменты JavaScript, такие как JSLint и Closure Compiler, очень громко рассказывают вам, когда вы вводите неверный тип в имени переменной перечисления. CodeMirror, браузер и различные инструменты Javascript вместе делают отладку этой формы перечисления очень простой и действительно простой.

Демонстрация подсветки CodeMirror

const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;
var currentColor = ENUM_COLORENUM_GREEN;

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

if(currentColor === ENUM_COLORENUM_DNE) {
   // whatever
}

В приведенном выше фрагменте вы были предупреждены об ошибке, потому ENUM_COLORENUM_DNEчто не существует.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Я думаю, можно с уверенностью сказать, что эта методология перечисления действительно является лучшим способом не только для минимизированного размера кода, но также для производительности, отладки и совместной работы.

Прочитав полезный вопрос, я благодарю автора за то, что он потратил время на их написание, щелкнув верхнюю левую стрелку вверх в окне вопроса. Каждое поле ответа также имеет одну из этих стрелок вверх.


Эх. Я настоятельно предпочитаю удобочитаемость и простоту использования и понимания размера кода.
Андрей

1
@ Андрей С моим ответом вы можете иметь оба. Мой ответ дает самый простой в использовании / управлении код и наименьший минимизированный размер кода.
Джек Гиффин

1
@ Андрей Я попытался применить твой еще один список (ДА!) К примеру с анализатором цвета в моем ответе. Однако я обнаружил несколько проблем, которые вы можете решить. У YEA нет возможности расширить перечисления подклассами, что вынуждает меня создавать отдельные родительские и дочерние классы, которыми может быть довольно сложно управлять в больших проектах. YEA не гарантирует, что запись существует (бывшая colors.REEDдоходность undefined), поэтому опечатки создают неуловимые загадки. YEA не различает использование перечислений в качестве индексов и идентификаторов, что приводит к запутанному коду, где все выглядит одинаково. …
Джек Гиффин

1
@Andrew… YEA мешает компиляции Closure Compiler минимизировать. Сравните исходный код с YEA (3549 байт) с минимизированным кодом с YEA (1344 байт) с минимизированным кодом с моим решением (604 байт). Наконец, YEA включает в себя «отображение по имени», поскольку оно отделяет имена строк от перечисляемых идентификаторов. Мой учитывает только ID, поэтому «отображение по имени» не требуется, что ведет к упрощению дизайна и повышению производительности. Спасибо за то, что поделились своим решением, но для его практического применения необходимо много исправлений.
Джек Гиффин

1
@ Андрей. Ты имеешь право на свое мнение так же, как и я mine
Джек Гиффин,

23

Я играл с этим, так как я люблю свои перечисления. знак равно

Используя, Object.definePropertyя думаю, я нашел несколько жизнеспособное решение.

Вот jsfiddle: http://jsfiddle.net/ZV4A6/

Используя этот метод ... вы должны (в теории) иметь возможность вызывать и определять значения перечисления для любого объекта, не затрагивая другие атрибуты этого объекта.

Object.defineProperty(Object.prototype,'Enum', {
    value: function() {
        for(i in arguments) {
            Object.defineProperty(this,arguments[i], {
                value:parseInt(i),
                writable:false,
                enumerable:true,
                configurable:true
            });
        }
        return this;
    },
    writable:false,
    enumerable:false,
    configurable:false
}); 

Из-за атрибута writable:falseэто должно сделать его безопасным.

Таким образом, вы должны иметь возможность создать пользовательский объект, а затем вызвать Enum()его. Присвоенные значения начинаются с 0 и увеличиваются на единицу.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

3
Если вы добавите return this;в конце Enum, вы можете сделать:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

Я не учел это, поскольку это не мой обычный метод ведения дел. Но ты абсолютно прав! Я отредактирую это в.
Дункан

Мне очень нравится это, хотя я не большой поклонник осквернения пространства объектов (с глобальной функцией ENUM). Преобразовал это в функцию mkenum и добавил необязательные числовые назначения => var mixedUp = mkenum ('ЧЕРНЫЙ', {КРАСНЫЙ: 0x0F00, СИНИЙ: 0X0F, ЗЕЛЕНЫЙ: 0x0F0, БЕЛЫЙ: 0x0FFF, ONE: 1}, TWO, THREE, FOUR) ; // Добавление моего кода в качестве ответа ниже. Спасибо.
Андрей Филипс

Если честно, я даже больше этим не пользуюсь. Я использовал Google Closure Compiler, и он работает не слишком хорошо (или просто усложняет ситуацию), если вы используете расширенные настройки. Итак, я только что вернулся к стандартной записи объектов.
Дункан

1
falseпо умолчанию для writable, enumerableи configurable. Нет необходимости переваривать значения по умолчанию.
празднование

23

Используйте Javascript Proxies

TLDR: добавьте этот класс в свои служебные методы и используйте его во всем коде, он имитирует поведение Enum из традиционных языков программирования и фактически выдает ошибки, когда вы пытаетесь получить доступ к не существующему перечислителю или добавить / обновить перечислитель. Не нужно полагаться на Object.freeze().

class Enum {
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name];
        }
        throw new Error(`No such enumerator: ${name}`);
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    };

    return new Proxy(enumObj, handler);
  }
}

Затем создайте перечисления, создав экземпляр класса:

const roles = new Enum({
  ADMIN: 'Admin',
  USER: 'User',
});

Полное объяснение:

Одна очень полезная особенность Enums, которую вы получаете от традиционных языков, заключается в том, что они взрываются (выдают ошибку времени компиляции), если вы пытаетесь получить доступ к перечислителю, который не существует.

Помимо замораживания поддельной структуры enum для предотвращения случайного / злонамеренного добавления дополнительных значений, ни один из других ответов не затрагивает эту внутреннюю особенность Enums.

Как вы, вероятно, знаете, доступ к несуществующим элементам в JavaScript просто возвращает undefinedи не взрывает ваш код. Поскольку перечислители являются предопределенными константами (т.е. днями недели), никогда не должно быть случая, когда перечислитель должен быть неопределенным.

Не поймите меня неправильно, поведение JavaScript при возврате undefinedпри доступе к неопределенным свойствам на самом деле является очень мощной функцией языка, но это не та функция, которая вам нужна, когда вы пытаетесь смоделировать традиционные структуры Enum.

Здесь прокси объекты сияют. Прокси были стандартизированы в языке с введением ES6 (ES2015). Вот описание из MDN:

Объект Proxy используется для определения пользовательского поведения для основных операций (например, поиск свойств, присваивание, перечисление, вызов функции и т. Д.).

Подобно прокси-серверу веб-сервера, прокси-серверы JavaScript способны перехватывать операции над объектами (с использованием «ловушек», называть их хуками, если хотите) и позволяют выполнять различные проверки, действия и / или манипуляции до их завершения (или в некоторых случаях вообще прекращение операций, что мы и хотим делать, если и когда мы пытаемся сослаться на перечислитель, который не существует).

Вот надуманный пример, который использует объект Proxy для имитации Enums. В этом примере перечислители являются стандартными методами HTTP (например, «GET», «POST» и т. Д.):

// Class for creating enums (13 lines)
// Feel free to add this to your utility library in 
// your codebase and profit! Note: As Proxies are an ES6 
// feature, some browsers/clients may not support it and 
// you may need to transpile using a service like babel

class Enum {
  // The Enum class instantiates a JavaScript Proxy object.
  // Instantiating a `Proxy` object requires two parameters, 
  // a `target` object and a `handler`. We first define the handler,
  // then use the handler to instantiate a Proxy.

  // A proxy handler is simply an object whose properties
  // are functions which define the behavior of the proxy 
  // when an operation is performed on it. 
  
  // For enums, we need to define behavior that lets us check what enumerator
  // is being accessed and what enumerator is being set. This can be done by 
  // defining "get" and "set" traps.
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name]
        }
        throw new Error(`No such enumerator: ${name}`)
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    }


    // Freeze the target object to prevent modifications
    return new Proxy(enumObj, handler)
  }
}


// Now that we have a generic way of creating Enums, lets create our first Enum!
const httpMethods = new Enum({
  DELETE: "DELETE",
  GET: "GET",
  OPTIONS: "OPTIONS",
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT"
})

// Sanity checks
console.log(httpMethods.DELETE)
// logs "DELETE"

try {
  httpMethods.delete = "delete"
} catch (e) {
console.log("Error: ", e.message)
}
// throws "Cannot add/update properties on an Enum instance after it is defined"

try {
  console.log(httpMethods.delete)
} catch (e) {
  console.log("Error: ", e.message)
}
// throws "No such enumerator: delete"


ASIDE: Какого черта это прокси?

Я помню, когда я впервые начал видеть слово «прокси» везде, это определенно не имело смысла для меня долгое время. Если вы сейчас, я думаю, что простой способ обобщить прокси - это думать о них как о программном обеспечении, учреждениях или даже людях, которые выступают в качестве посредников или посредников между двумя серверами, компаниями или людьми.


Как сделать что-то вроде myEnum.valueOf ("someStringValue")? Ожидаемое: в случае, если входная строка имеет значение элемента перечислителя, должен вернуть элемент. Если ни у одного элемента нет этого строкового значения, выдается исключение.
sscarduzio

@sscarduzio вы можете переопределить valueOfметод по умолчанию , указав его как метод экземпляра в классе Enum. Однако, почему вы хотите получить к нему доступ таким образом, а не просто с помощью точечной нотации?
Говинд Рай

Мое перечисление const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}), и я анализирую при вводе произвольную строку "info" или "debug". Поэтому мне нужно что-то вроде currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio

1
Почему ты не мог просто сделать logLevelEnum[settings.get("log_level")]? добавление parseOrThrowбудет просто повторением того, что ловушки прокси уже делают для вас.
Говинд Рай

17

Это старая версия, которую я знаю, но с тех пор она была реализована через интерфейс TypeScript:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Foo"] = 0] = "Foo";
    MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
    MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));

Это позволяет вам искать оба, MyEnum.Barкоторые возвращают 1, и MyEnum[1]которые возвращают «Bar» независимо от порядка объявления.


1
Плюс работает MyEnum ["Bar"], который пока возвращает 1 ... <3 TypeScript ...
David Karlaš

3
и, конечно, если вы на самом деле используете Typescript:enum MyEnum { Foo, Bar, Foobar }
Парламент

16

В ES7 вы можете сделать элегантный ENUM, опираясь на статические атрибуты:

class ColorEnum  {
    static RED = 0 ;
    static GREEN = 1;
    static BLUE = 2;
}

тогда

if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}

Преимущество (использование класса вместо буквального объекта) должен иметь родительский класс , Enumто все ваши Перечисления будут расширяет этот класс.

 class ColorEnum  extends Enum {/*....*/}

4
Не могли бы вы объяснить, почему наличие родительского класса является преимуществом, пожалуйста? Я чувствую, что что-то упустил!
Джон Г

7
Не делай этого. new ColorEnum()не имеет абсолютно никакого смысла.
Берги

3
расширение перечисления звучит безумно, правда
Codii

как только язык не поддерживает его изначально, имеет смысл придерживаться этого соглашения и использовать его вот так! согласен!
17

Я думаю (?), К чему стремится OP: Преимущество чистой статики в том, что она доступна везде как одиночка, и вам не нужно создавать экземпляр класса - OP не предлагает делать это! Я думаю , что он говорит, что суперкласс Enumимеет стандартные статические методы Enumerator на него, как getValues(), getNames(), iterate()и т.д. Если это так, вы не должны переопределять их для каждого нового вида enum.
инженер

15

Это решение, которое я использую.

function Enum() {
    this._enums = [];
    this._lookups = {};
}

Enum.prototype.getEnums = function() {
    return _enums;
}

Enum.prototype.forEach = function(callback){
    var length = this._enums.length;
    for (var i = 0; i < length; ++i){
        callback(this._enums[i]);
    }
}

Enum.prototype.addEnum = function(e) {
    this._enums.push(e);
}

Enum.prototype.getByName = function(name) {
    return this[name];
}

Enum.prototype.getByValue = function(field, value) {
    var lookup = this._lookups[field];
    if(lookup) {
        return lookup[value];
    } else {
        this._lookups[field] = ( lookup = {});
        var k = this._enums.length - 1;
        for(; k >= 0; --k) {
            var m = this._enums[k];
            var j = m[field];
            lookup[j] = m;
            if(j == value) {
                return m;
            }
        }
    }
    return null;
}

function defineEnum(definition) {
    var k;
    var e = new Enum();
    for(k in definition) {
        var j = definition[k];
        e[k] = j;
        e.addEnum(j)
    }
    return e;
}

И вы определяете свои перечисления следующим образом:

var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

И вот как вы получаете доступ к своим перечислениям:

COLORS.BLUE.string
COLORS.BLUE.value
COLORS.getByName('BLUE').string
COLORS.getByValue('value', 1).string

COLORS.forEach(function(e){
    // do what you want with e
});

Я обычно использую последние 2 метода для отображения перечислений из объектов сообщений.

Некоторые преимущества этого подхода:

  • Легко объявлять перечисления
  • Легко получить доступ к вашим перечислениям
  • Ваши перечисления могут быть сложными типами
  • Класс Enum имеет некоторое ассоциативное кэширование, если вы часто используете getByValue

Некоторые недостатки:

  • Там происходит некоторое грязное управление памятью, так как я храню ссылки на перечисления
  • Все еще нет типа безопасности

14

Создайте литерал объекта:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};

12
constне делает свойства объекта неизменяемыми, это только означает, что переменная Modesне может быть переназначена чему-либо другому. Чтобы сделать его более полным, используйте Object.freeze()рядом const.
rvighne

Пожалуйста, не используйте Object.freeze. Это препятствует тому, чтобы Закрытие Компилятора встраивало объект.
Джек Гиффин

11

Если вы используете Backbone , вы можете получить полную функциональность перечисления (поиск по идентификатору, имени, пользовательским элементам) бесплатно с помощью Backbone.Collection .

// enum instance members, optional
var Color = Backbone.Model.extend({
    print : function() {
        console.log("I am " + this.get("name"))
    }
});

// enum creation
var Colors = new Backbone.Collection([
    { id : 1, name : "Red", rgb : 0xFF0000},
    { id : 2, name : "Green" , rgb : 0x00FF00},
    { id : 3, name : "Blue" , rgb : 0x0000FF}
], {
    model : Color
});

// Expose members through public fields.
Colors.each(function(color) {
    Colors[color.get("name")] = color;
});

// using
Colors.Red.print()

8

ваши ответы слишком сложны

var buildSet = function(array) {
  var set = {};
  for (var i in array) {
    var item = array[i];
    set[item] = item;
  }
  return set;
}

var myEnum = buildSet(['RED','GREEN','BLUE']);
// myEnum.RED == 'RED' ...etc

1
@JackGiffin Я согласен с тем, что ваш ответ более производительный и мой может занять больше памяти, хотя вы не должны предполагать, что все хотят перечисления, как это реализовано в C ++. Пожалуйста, уважайте другие ответы и разработчиков, которые могут предпочесть этот, а не ваш.
Xeltor

7

Я изменил решение Андре 'Fi':

  function Enum() {
    var that = this;
    for (var i in arguments) {
        that[arguments[i]] = i;
    }
    this.name = function(value) {
        for (var key in that) {
            if (that[key] == value) {
                return key;
            }
        }
    };
    this.exist = function(value) {
        return (typeof that.name(value) !== "undefined");
    };
    if (Object.freeze) {
        Object.freeze(that);
    }
  }

Тестовое задание:

var Color = new Enum('RED', 'GREEN', 'BLUE');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true

6

Я придумал этот подход, который смоделирован после перечислений в Java. Они безопасны от типов, поэтому вы также можете выполнять instanceofпроверки.

Вы можете определить перечисления следующим образом:

var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);

DaysТеперь относится к Daysперечислению:

Days.Monday instanceof Days; // true

Days.Friday.name(); // "Friday"
Days.Friday.ordinal(); // 4

Days.Sunday === Days.Sunday; // true
Days.Sunday === Days.Friday; // false

Days.Sunday.toString(); // "Sunday"

Days.toString() // "Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } "

Days.values().map(function(e) { return e.name(); }); //["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
Days.values()[4].name(); //"Friday"

Days.fromName("Thursday") === Days.Thursday // true
Days.fromName("Wednesday").name() // "Wednesday"
Days.Friday.fromName("Saturday").name() // "Saturday"

Реализация:

var Enum = (function () {
    /**
     * Function to define an enum
     * @param typeName - The name of the enum.
     * @param constants - The constants on the enum. Can be an array of strings, or an object where each key is an enum
     * constant, and the values are objects that describe attributes that can be attached to the associated constant.
     */
    function define(typeName, constants) {

        /** Check Arguments **/
        if (typeof typeName === "undefined") {
            throw new TypeError("A name is required.");
        }

        if (!(constants instanceof Array) && (Object.getPrototypeOf(constants) !== Object.prototype)) {

            throw new TypeError("The constants parameter must either be an array or an object.");

        } else if ((constants instanceof Array) && constants.length === 0) {

            throw new TypeError("Need to provide at least one constant.");

        } else if ((constants instanceof Array) && !constants.reduce(function (isString, element) {
                return isString && (typeof element === "string");
            }, true)) {

            throw new TypeError("One or more elements in the constant array is not a string.");

        } else if (Object.getPrototypeOf(constants) === Object.prototype && !Object.keys(constants).reduce(function (isObject, constant) {
                return Object.getPrototypeOf(constants[constant]) === Object.prototype;
            }, true)) {

            throw new TypeError("One or more constants do not have an associated object-value.");

        }

        var isArray = (constants instanceof Array);
        var isObject = !isArray;

        /** Private sentinel-object used to guard enum constructor so that no one else can create enum instances **/
        function __() { };

        /** Dynamically define a function with the same name as the enum we want to define. **/
        var __enum = new Function(["__"],
            "return function " + typeName + "(sentinel, name, ordinal) {" +
                "if(!(sentinel instanceof __)) {" +
                    "throw new TypeError(\"Cannot instantiate an instance of " + typeName + ".\");" +
                "}" +

                "this.__name = name;" +
                "this.__ordinal = ordinal;" +
            "}"
        )(__);

        /** Private objects used to maintain enum instances for values(), and to look up enum instances for fromName() **/
        var __values = [];
        var __dict = {};

        /** Attach values() and fromName() methods to the class itself (kind of like static methods). **/
        Object.defineProperty(__enum, "values", {
            value: function () {
                return __values;
            }
        });

        Object.defineProperty(__enum, "fromName", {
            value: function (name) {
                var __constant = __dict[name]
                if (__constant) {
                    return __constant;
                } else {
                    throw new TypeError(typeName + " does not have a constant with name " + name + ".");
                }
            }
        });

        /**
         * The following methods are available to all instances of the enum. values() and fromName() need to be
         * available to each constant, and so we will attach them on the prototype. But really, they're just
         * aliases to their counterparts on the prototype.
         */
        Object.defineProperty(__enum.prototype, "values", {
            value: __enum.values
        });

        Object.defineProperty(__enum.prototype, "fromName", {
            value: __enum.fromName
        });

        Object.defineProperty(__enum.prototype, "name", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "ordinal", {
            value: function () {
                return this.__ordinal;
            }
        });

        Object.defineProperty(__enum.prototype, "valueOf", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "toString", {
            value: function () {
                return this.__name;
            }
        });

        /**
         * If constants was an array, we can the element values directly. Otherwise, we will have to use the keys
         * from the constants object.
         */
        var _constants = constants;
        if (isObject) {
            _constants = Object.keys(constants);
        }

        /** Iterate over all constants, create an instance of our enum for each one, and attach it to the enum type **/
        _constants.forEach(function (name, ordinal) {
            // Create an instance of the enum
            var __constant = new __enum(new __(), name, ordinal);

            // If constants was an object, we want to attach the provided attributes to the instance.
            if (isObject) {
                Object.keys(constants[name]).forEach(function (attr) {
                    Object.defineProperty(__constant, attr, {
                        value: constants[name][attr]
                    });
                });
            }

            // Freeze the instance so that it cannot be modified.
            Object.freeze(__constant);

            // Attach the instance using the provided name to the enum type itself.
            Object.defineProperty(__enum, name, {
                value: __constant
            });

            // Update our private objects
            __values.push(__constant);
            __dict[name] = __constant;
        });

        /** Define a friendly toString method for the enum **/
        var string = typeName + " { " + __enum.values().map(function (c) {
                return c.name();
            }).join(", ") + " } ";

        Object.defineProperty(__enum, "toString", {
            value: function () {
                return string;
            }
        });

        /** Freeze our private objects **/
        Object.freeze(__values);
        Object.freeze(__dict);

        /** Freeze the prototype on the enum and the enum itself **/
        Object.freeze(__enum.prototype);
        Object.freeze(__enum);

        /** Return the enum **/
        return __enum;
    }

    return {
        define: define
    }

})();

Это выглядит красиво, может быть, вам следует проверить наличие freezeметода обратной совместимости? Например,if (Object.freeze) { Object.freeze(values); }
FBB

Хорошая точка зрения! Сделаю!
Вивин Палиат

6

IE8 не поддерживает метод freeze ().
Источник: http://kangax.github.io/compat-table/es5/ , нажмите «Показать устаревшие браузеры?» сверху, и проверьте IE8 и зафиксируйте пересечение столбцов строк.

В моем текущем игровом проекте я использовал ниже, так как мало клиентов все еще используют IE8:

var CONST_WILD_TYPES = {
    REGULAR: 'REGULAR',
    EXPANDING: 'EXPANDING',
    STICKY: 'STICKY',
    SHIFTING: 'SHIFTING'
};

Мы также могли бы сделать:

var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

или даже это:

var CONST_WILD_TYPES = {
    REGULAR: '1',
    EXPANDING: '2',
    STICKY: '3',
    SHIFTING: '4'
};

Последнее, кажется, наиболее эффективно для строки, оно уменьшает вашу общую пропускную способность, если у вас есть сервер и клиент, обменивающийся этими данными.
Конечно, теперь вы обязаны убедиться, что в данных нет конфликтов (RE, EX и т. Д. Должны быть уникальными, а также 1, 2 и т. Д. Должны быть уникальными). Обратите внимание, что вы должны поддерживать их всегда для обратной совместимости.

Назначение:

var wildType = CONST_WILD_TYPES.REGULAR;

Comparision:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}

5
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

Вам не нужно следить за тем, чтобы таким образом не назначать повторяющиеся числа различным значениям перечисления. Новый объект создается и присваивается всем значениям перечисления.


Этот ответ недооценен. Это одна из моих любимых идей из-за своей простоты. На практике, я думаю, что я буду придерживаться строк, потому что сейчас легче отлаживать.
Домино

Хм, просто убедитесь, что этот код не вызывается дважды ...
Эндрю

4

Вот несколько разных способов реализации перечислений TypeScript .

Самый простой способ - просто перебрать объект, добавив к нему инвертированные пары ключ-значение. Единственным недостатком является то, что вы должны вручную установить значение для каждого члена.

function _enum(list) {       
  for (var key in list) {
    list[list[key] = list[key]] = key;
  }
  return Object.freeze(list);
}

var Color = _enum({
  Red: 0,
  Green: 5,
  Blue: 2
});

// Color → {0: "Red", 2: "Blue", 5: "Green", "Red": 0, "Green": 5, "Blue": 2}
// Color.Red → 0
// Color.Green → 5
// Color.Blue → 2
// Color[5] → Green
// Color.Blue > Color.Green → false


А вот микшин lodash для создания перечисления с использованием строки. Хотя эта версия немного сложнее, она выполняет нумерацию автоматически. Все методы lodash, используемые в этом примере, имеют обычный эквивалент JavaScript, так что вы можете легко их отключить, если хотите.

function enum() {
    var key, val = -1, list = {};
    _.reduce(_.toArray(arguments), function(result, kvp) {    
        kvp = kvp.split("=");
        key = _.trim(kvp[0]);
        val = _.parseInt(kvp[1]) || ++val;            
        result[result[val] = key] = val;
        return result;
    }, list);
    return Object.freeze(list);
}    

// Add enum to lodash 
_.mixin({ "enum": enum });

var Color = _.enum(
    "Red",
    "Green",
    "Blue = 5",
    "Yellow",
    "Purple = 20",
    "Gray"
);

// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue

очень умно, спасибо
Илан

4

Я только что опубликовал пакет NPM gen_enum, позволяющий быстро создавать структуру данных Enum в Javascript:

var genEnum = require('gen_enum');

var AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD');
var curMode = AppMode.LOG_IN;
console.log(curMode.isLogIn()); // output true 
console.log(curMode.isSignUp()); // output false 
console.log(curMode.isForgotPassword()); // output false 

В этом маленьком инструменте есть одна приятная вещь: в современной среде (включая nodejs и браузеры IE 9+) возвращаемый объект Enum является неизменным.

Для получения дополнительной информации, пожалуйста, проверьте https://github.com/greenlaw110/enumjs

Обновления

Устаревший gen_enumпакет и объединяю функцию в пакет constjs , который предоставляет больше возможностей, включая неизменяемые объекты, десериализацию строк JSON, строковые константы, генерацию растровых изображений и т. Д. Оформление заказа https://www.npmjs.com/package/constjs Для получения дополнительной информации

Чтобы обновить , gen_enumчтобы constjsпросто изменить заявление

var genEnum = require('gen_enum');

в

var genEnum = require('constjs').enum;

4

Самое простое решение:

Создайте

var Status = Object.freeze({
    "Connecting":0,
    "Ready":1,
    "Loading":2,
    "Processing": 3
});

Получить значение

console.log(Status.Ready) // 1

Получить ключ

console.log(Object.keys(Status)[Status.Ready]) // Ready

4

Я создал класс Enum, который может извлекать значения и имена в O (1). Он также может генерировать массив объектов, содержащий все имена и значения.

function Enum(obj) {
    // Names must be unique, Values do not.
    // Putting same values for different Names is risky for this implementation

    this._reserved = {
        _namesObj: {},
        _objArr: [],
        _namesArr: [],
        _valuesArr: [],
        _selectOptionsHTML: ""
    };

    for (k in obj) {
        if (obj.hasOwnProperty(k)) {
            this[k] = obj[k];
            this._reserved._namesObj[obj[k]] = k;
        }
    }
}
(function () {
    this.GetName = function (val) {
        if (typeof this._reserved._namesObj[val] === "undefined")
            return null;
        return this._reserved._namesObj[val];
    };

    this.GetValue = function (name) {
        if (typeof this[name] === "undefined")
            return null;
        return this[name];
    };

    this.GetObjArr = function () {
        if (this._reserved._objArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push({
                            Name: k,
                            Value: this[k]
                        });
            }
            this._reserved._objArr = arr;
        }
        return this._reserved._objArr;
    };

    this.GetNamesArr = function () {
        if (this._reserved._namesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(k);
            }
            this._reserved._namesArr = arr;
        }
        return this._reserved._namesArr;
    };

    this.GetValuesArr = function () {
        if (this._reserved._valuesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(this[k]);
            }
            this._reserved._valuesArr = arr;
        }
        return this._reserved._valuesArr;
    };

    this.GetSelectOptionsHTML = function () {
        if (this._reserved._selectOptionsHTML.length == 0) {
            var html = "";
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        html += "<option value='" + this[k] + "'>" + k + "</option>";
            }
            this._reserved._selectOptionsHTML = html;
        }
        return this._reserved._selectOptionsHTML;
    };
}).call(Enum.prototype);

Вы можете init'd это так:

var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

Чтобы получить значение (как Enums в C #):

var val2 = enum1.item2;

Чтобы получить имя для значения (может быть неоднозначным, если поставить одно и то же значение для разных имен):

var name1 = enum1.GetName(0);  // "item1"

Чтобы получить массив с каждым именем и значением в объекте:

var arr = enum1.GetObjArr();

Будет генерировать:

[{ Name: "item1", Value: 0}, { ... }, ... ]

Вы также можете легко получить опции выбора HTML:

var html = enum1.GetSelectOptionsHTML();

Который содержит:

"<option value='0'>item1</option>..."

4

Даже если только статические методы (а не статические свойства) поддерживаются в ES2015 (см здесь также, §15.2.2.2), как ни странно , вы можете использовать ниже с Вавилонской с es2015предустановки:

class CellState {
    v: string;
    constructor(v: string) {
        this.v = v;
        Object.freeze(this);
    }
    static EMPTY       = new CellState('e');
    static OCCUPIED    = new CellState('o');
    static HIGHLIGHTED = new CellState('h');
    static values      = function(): Array<CellState> {
        const rv = [];
        rv.push(CellState.EMPTY);
        rv.push(CellState.OCCUPIED);
        rv.push(CellState.HIGHLIGHTED);
        return rv;
    }
}
Object.freeze(CellState);

Я обнаружил, что это работает должным образом даже между модулями (например, импорт CellStateenum из другого модуля), а также при импорте модуля с помощью Webpack.

Преимущество этого метода перед большинством других ответов заключается в том, что вы можете использовать его вместе со средством проверки статического типа (например, Flow ), и вы можете утверждать, что во время разработки, используя проверку статического типа, что ваши переменные, параметры и т. Д. Имеют определенную CellState" enum ", а не какой-либо другой enum (который было бы невозможно отличить, если бы вы использовали общие объекты или символы).

Обновить

Вышеприведенный код имеет недостаток в том, что он позволяет создавать дополнительные объекты типа CellState(даже если вы не можете назначить их статическим полям, CellStateтак как он заморожен). Тем не менее, приведенный ниже более усовершенствованный код предлагает следующие преимущества:

  1. больше CellStateнельзя создавать объекты типа
  2. вам гарантировано, что никаким двум экземплярам enum не присвоен одинаковый код
  3. служебный метод для получения перечисления из строкового представления
  4. valuesфункция , которая возвращает все экземпляры перечисления не должны создавать возвращаемое значение в приведенном выше, ручной (и подверженной ошибкам) способом.

    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;

Хороший пример :-)
Ашраф.Шк786

4

es7 way, (iterator, freeze), использование:

const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')

for (let name of ThreeWiseMen)
    console.log(name)


// with a given key
let key = ThreeWiseMen.Melchior

console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)

for (let entry from key.enum)
     console.log(entry)


// prevent alteration (throws TypeError in strict mode)
ThreeWiseMen.Me = 'Me too!'
ThreeWiseMen.Melchior.name = 'Foo'

код:

class EnumKey {

    constructor(props) { Object.freeze(Object.assign(this, props)) }

    toString() { return this.name }

}

export class Enum {

    constructor(...keys) {

        for (let [index, key] of keys.entries()) {

            Object.defineProperty(this, key, {

                value: new EnumKey({ name:key, index, enum:this }),
                enumerable: true,

            })

        }

        Object.freeze(this)

    }

    *[Symbol.iterator]() {

        for (let key of Object.keys(this))
            yield this[key]

    }

    toString() { return [...this].join(', ') }

}

4

Вот как Typescript переводит это enumв Javascript:

var makeEnum = function(obj) {
    obj[ obj['Active'] = 1 ] = 'Active';
    obj[ obj['Closed'] = 2 ] = 'Closed';
    obj[ obj['Deleted'] = 3 ] = 'Deleted';
}

Сейчас же:

makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}

Сначала я был озадачен, почему obj[1]возвращается 'Active', но потом понял, что его очень просто - оператор присваивания присваивает значение, а затем возвращает его:

obj['foo'] = 1
// => 1

4

Вы можете сделать что-то вроде этого

    var Enum = (function(foo) {

    var EnumItem = function(item){
        if(typeof item == "string"){
            this.name = item;
        } else {
            this.name = item.name;
        }
    }
    EnumItem.prototype = new String("DEFAULT");
    EnumItem.prototype.toString = function(){
        return this.name;
    }
    EnumItem.prototype.equals = function(item){
        if(typeof item == "string"){
            return this.name == item;
        } else {
            return this == item && this.name == item.name;
        }
    }

    function Enum() {
        this.add.apply(this, arguments);
        Object.freeze(this);
    }
    Enum.prototype.add = function() {
        for (var i in arguments) {
            var enumItem = new EnumItem(arguments[i]);
            this[enumItem.name] = enumItem;
        }
    };
    Enum.prototype.toList = function() {
        return Object.keys(this);
    };
    foo.Enum = Enum;
    return Enum;
})(this);
var STATUS = new Enum("CLOSED","PENDING", { name : "CONFIRMED", ackd : true });
var STATE = new Enum("CLOSED","PENDING","CONFIRMED",{ name : "STARTED"},{ name : "PROCESSING"});

Как определено в этой библиотеке. https://github.com/webmodule/foo/blob/master/foo.js#L217

Полный пример https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026


3

Быстрый и простой способ будет:

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

console.log(Colors.WHITE)  //this prints out "0"

6
Эта функция не нужна и дает вам тот же результат, что и ОП.
Сильдорет

3

На момент написания, октябрь 2014 года - так вот современное решение. Пишу решение в виде Node Module и включаю тест с использованием Mocha и Chai, а также underscoreJS. Вы можете легко игнорировать их и просто взять код Enum, если хотите.

Посмотрел много постов с чрезмерно запутанными библиотеками и т. Д. Решение получить поддержку enum в Javascript настолько просто, что действительно не нужно. Вот код:

Файл: enums.js

_ = require('underscore');

var _Enum = function () {

   var keys = _.map(arguments, function (value) {
      return value;
   });
   var self = {
      keys: keys
   };
   for (var i = 0; i < arguments.length; i++) {
      self[keys[i]] = i;
   }
   return self;
};

var fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));
var encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));

exports.fileFormatEnum = fileFormatEnum;
exports.encodingEnum = encodingEnum;

И тест, чтобы проиллюстрировать, что это дает вам:

файл: enumsSpec.js

var chai = require("chai"),
    assert = chai.assert,
    expect = chai.expect,
    should = chai.should(),
    enums = require('./enums'),
    _ = require('underscore');


describe('enums', function () {

    describe('fileFormatEnum', function () {
        it('should return expected fileFormat enum declarations', function () {
            var fileFormatEnum = enums.fileFormatEnum;
            should.exist(fileFormatEnum);
            assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');
            assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');
        });
    });

    describe('encodingEnum', function () {
        it('should return expected encoding enum declarations', function () {
            var encodingEnum = enums.encodingEnum;
            should.exist(encodingEnum);
            assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format');
            assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');
        });
    });

});

Как видите, вы получаете фабрику Enum, вы можете получить все ключи, просто вызвав enum.keys, и вы можете сопоставить сами ключи с целочисленными константами. И вы можете повторно использовать фабрику с другими значениями и экспортировать сгенерированные Enums, используя модульный подход Node.

Еще раз, если вы обычный пользователь, или в браузере и т. Д., Просто возьмите заводскую часть кода, потенциально удалив также библиотеку подчеркивания, если вы не хотите использовать ее в своем коде.


5
Не могли бы вы опубликовать ответ, просто "вот как это сделать как обычный пользователь, который просто хочет перечисления, а не фабрики, подчеркивания или что-то необычное"?
GreenAsJade

5
Несмотря на то, что это довольно круто с точки зрения разработчиков, оно не очень чистое и не читаемое. Решение Enum от OP легче и удобочитаемее во всех отношениях, поэтому его лучше использовать. Тем не менее, довольно круто, что вы придумали это.
Дэвид

3

Это легко использовать, я думаю. https://stackoverflow.com/a/32245370/4365315

var A = {a:11, b:22}, 
enumA = new TypeHelper(A);

if(enumA.Value === A.b || enumA.Key === "a"){ 
... 
}

var keys = enumA.getAsList();//[object, object]

//set
enumA.setType(22, false);//setType(val, isKey)

enumA.setType("a", true);

enumA.setTypeByIndex(1);

ОБНОВИТЬ:

Есть мои вспомогательные коды ( TypeHelper).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.