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

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

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

// later on

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

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

+1911
13 нояб. '08 в 19:09
источник поделиться
47 ответов
  • 1
  • 2

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

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

или же

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

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

Примечание: я написал это в 2011 году, но это 2019 - используйте const для предотвращения перезаписи вашего словаря enum.

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

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

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

Источник

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

+742
18 февр. '11 в 11:03
источник

Связанные вопросы


Похожие вопросы

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

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

+594
13 нояб. '08 в 19:13
источник

ОБНОВЛЕНИЕ. Спасибо всем за все, что мне нравится, но я не думаю, что мой ответ ниже - лучший способ написать перечисления в 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, так как это динамический язык, возможно даже добавить значения перечисления в набор позже:

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

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

// Add 'Extra Large' size, only knowing it 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.        
  }
} 

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

+488
04 мар. '10 в 22:31
источник

Нижняя строка: вы не можете.

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

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

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

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

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

Изменить

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

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

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

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

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

Хотя это, вероятно, не подходит для любого действительного использования перечислений, это идет очень долго.

+80
21 авг. '09 в 20: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');
+55
13 июл. '11 в 0:28
источник

В большинстве современных браузеров существует тип данных символьного примитива, который можно использовать для создания перечисления. Это обеспечит безопасность типов перечисления, так как каждое значение символа гарантировано 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
+47
05 мая '15 в 16:32
источник

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

Использование 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
+22
21 авг. '13 в 10:34
источник

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




Underscore-Notation Variables

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

// later on

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




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

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

Addition extension diagram

(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_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. Обратите внимание.




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

Wringing code via 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 байт)




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

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

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

Chart of code sizes



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




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

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

// 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
}

𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 OoOrv.png

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

lattice.svg

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




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

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

CodeMirror highlighting demonstration

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
}




𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

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

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

+21
15 мая '18 в 16:53
источник

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

TL;DR: добавьте этот класс в свои служебные методы и используйте его во всем коде, он имитирует поведение 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: Какого черта это прокси?

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

+17
15 мар. '18 в 21:09
источник

Это старый, который я знаю, но способ, которым он был реализован через интерфейс 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" независимо от порядка объявления.

+17
24 июн. '13 в 16:11
источник

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

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

то

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

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

 class ColorEnum  extends Enum {/*....*/}
+15
06 янв. '17 в 7:07
источник

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

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
+14
26 июл. '15 в 12:03
источник

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

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
15 мая '12 в 9:26
источник

Если вы используете Backbone, вы можете бесплатно получить полнофункциональную функцию перечисления (найти по id, name, custom members), используя 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()
+11
24 апр. '12 в 14:04
источник

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

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
+8
15 мая '14 в 4:20
источник

Я изменил решение Andre '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
+7
11 апр. '13 в 12:11
источник

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
}
+6
02 июл. '15 в 13:03
источник

Я придумал этот подход, который смоделирован после перечислений в 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
    }

})();
+6
18 сент. '15 в 18:08
источник

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

    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

+4
25 мая '16 в 6:32
источник

Я создал класс Enum, который может выбирать значения AND и имена в 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);

Вы можете запустить его так:

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

Чтобы получить значение (например, Enums in С#):

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
06 янв. '16 в 15:20
источник

Несмотря на то что в ES2015 поддерживаются только статические методы (а не статические свойства) (см. здесь, а также раздел 15.2.2.2), с любопытством вы можете использовать ниже с Babel с предустановкой 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);

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

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

Обновление

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

  • не может быть создано больше объектов типа CellState
  • вам гарантировано, что двум экземплярам enum не присвоен тот же код
  • метод утилиты, чтобы вернуть перечисление из строкового представления
  • Функция 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;
    
+4
30 авг. '16 в 8:16
источник

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
14 февр. '18 в 23:44
источник
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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

+4
29 янв. '15 в 1:32
источник

Вот как 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
01 мая '18 в 11:32
источник

Я только что опубликовал пакет 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 

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

За дополнительной информацией обращайтесь https://github.com/greenlaw110/enumjs

Обновление

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

Для обновления с gen_enum до constjs просто измените оператор

var genEnum = require('gen_enum');

к

var genEnum = require('constjs').enum;
+4
27 апр. '15 в 11:13
источник

Здесь несколько разных способов реализовать TypeScript enums.

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

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 mixin для создания перечисления с использованием строки. Хотя эта версия немного больше задействована, она автоматически выполняет нумерацию. Все методы 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
+3
16 мар. '15 в 10:42
источник

С момента написания, Октябрь 2014 - так вот современное решение. Я пишу это решение как модуль Node и включил тест с использованием Mocha и Chai, а также underscoreJS. Вы можете легко проигнорировать их и просто взять код 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;

И тест, иллюстрирующий то, что он дает вам:

file: 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 factory, вы можете получить все ключи просто, вызвав enum.keys, и вы сами можете сопоставить ключи с целыми константами. И вы можете повторно использовать factory с разными значениями и экспортировать созданные сгенерированные суммы с использованием модульного подхода Node.

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

+3
04 мая '14 в 11:14
источник

Я написал enumerationjs a очень маленькую библиотеку для решения проблемы, которая обеспечивает безопасность типов > , разрешить константы перечисления наследовать от прототипа, гарантирует, что константы перечисления и типы перечислений должны быть неизменными + много маленьких функций. Он позволяет реорганизовать много кода и переместить некоторую логику в определении перечисления. Вот пример:

var CloseEventCodes = new Enumeration("closeEventCodes", {
  CLOSE_NORMAL:          { _id: 1000, info: "Connection closed normally" },
  CLOSE_GOING_AWAY:      { _id: 1001, info: "Connection closed going away" },
  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: "Connection closed due to protocol error"  },
  CLOSE_UNSUPPORTED:     { _id: 1003, info: "Connection closed due to unsupported operation" },
  CLOSE_NO_STATUS:       { _id: 1005, info: "Connection closed with no status" },
  CLOSE_ABNORMAL:        { _id: 1006, info: "Connection closed abnormally" },
  CLOSE_TOO_LARGE:       { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
    console.log(this.info); 
  }
});


CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true

Enumeration в основном представляет собой factory.

Полностью зарегистрированное руководство доступно здесь. Надеюсь, это поможет.

+3
25 окт. '15 в 4:03
источник

Простейшее решение:

Создать

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
+3
17 авг. '16 в 12:38
источник

Быстрый и простой способ:

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

console.log(Colors.WHITE)  //this prints out "0"
+3
01 окт. '13 в 9:02
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос