Який переважний синтаксис для визначення перерахунків у JavaScript?


2082

Який переважний синтаксис для визначення перерахунків у JavaScript? Щось на зразок:

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

// later on

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

Або є краща ідіома?


131
Не використовуйте 0як перелічувальний номер. Якщо тільки він не використовується для чогось, що не було встановлено. JS розглядає false || undefined || null || 0 || "" || '' || NaNвсі як однакове значення у порівнянні з використанням ==.
мацько

152
@matsko - це не просто аргумент проти використання ==?
sdm350

6
0 == nullповертає помилково
mcont

11
Але false == 0і +null == 0(і перетворення на номери трапляються іноді, коли ви цього не очікуєте), в той час як null == undefinedі +undefinedє NaN(хоча NaN != NaN).
sanderd17

46
Матриця подвійної рівності є більш заплутаною, ніж автоматичне форматування слова Microsoft
aaaaaa

Відповіді:


895

Оскільки 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
Відповідно до Wikipedia ( 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": {}}]який не буде працювати.
jcollum

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});
Сілдорет

5
@TolgaE дякую за цю бібліотеку! Це надихнуло мене не тільки звести його до мінімального рівня, але і додати пару функцій! Я розпрощався з
Supuhstar

3
@Supuhstar Це чудово! Я радий, що ти можеш скористатись нею. Сміливо зробиш запит на витяг, якщо ти хочеш, щоб він був об’єднаний у цій бібліотеці, тоді я можу оновити бібліотеку npm
Tolga E

2
Якщо когось цікавить, я реалізував безпечні для типу типи, схожі на те, як вони є на Java. Це означає, що ви можете робити instanceofперевірки. Наприклад ColorEnum.RED instanceof ColorEnum(повертає true). Ви також можете вирішити екземпляр із імені ColorEnum.fromName("RED") === ColorEnum.RED(повертається true). Кожен екземпляр також має a .name()і .ordinal()метод, а сам enum має values()метод, який повертає масив усіх констант.
Вівін Паліяф

3
Я не впевнений, що згоден з пропозицією "змістовного рядка". Енумів не слід розглядати як рядки чи числа; вони є абстрактними типами даних. "Вивести поточне значення" не повинно бути неможливим без якогось допоміжного методу. У Java та .NET - його ToString()метод. Ми, JS Devs, вже надто залежні від речей, "просто працюючих"! Крім того, треба мати можливість швидко switchпереживати. Порівняння рядків відбувається повільніше, ніж порівняння чисел, тому ви отримаєте дещо гірші switchпоказники, якщо використовувати рядки замість цілих чисел.
Rabadash8820

501

ОНОВЛЕННЯ

Дякую за всі відгуки всім, але я не думаю, що моя відповідь нижче - це найкращий спосіб писати переписки в JavaScript. Докладнішу інформацію див. У моєму блозі: Enums у 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"};

Пам'ятайте, що поля перерахунку (значення, ім'я та код у цьому прикладі) не потрібні для перевірки ідентичності та є лише для зручності. Також назву власності розміру не потрібно сильно кодувати, але також можна задавати динамічно. Отже, припустимо, що ви знаєте лише ім’я для нового значення перерахунку, ви все одно можете додати його без проблем:

// 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 для посилання на вашу публікацію з підходом до властивостей. Елегантний тим, що основні декларації прості, як і в ОП, із додатковими властивостями, якщо бажано.
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
кенгакс

2
@Scott Evernden: точка взята. @kangax: справа в тому, що це все-таки хак. Переліки просто не існують у Javascript, періоді та кінці історії. Навіть візерунок, запропонований Тімом Сильвестром, все ще є менш ідеальним злом.
Рандольфо

2
Посипання коду буквалами не дуже доцільне, тому для нього є сенс створювати константи. Звичайно, у Javascript також немає констант. Тому в основному це лише спосіб написання чистого коду. Це не може бути примусово застосовано, але в JavaScript може не дуже. Ви можете заново визначити константи, або функції, або в основному будь-що. EG: document.getElementById = function () {alert ("Ви накручені. Javascript не вводиться безпечно.");};
Штійн де Вітт

3
@Randolpho: А як щодо Object.freeze Artur Czajka? Чи не вдасться це завадити встановити понеділок на четвер?
Майкл - Де Клей Ширкий

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 (та OP) Я створив покращену версію на основі цієї відповіді: stackoverflow.com/a/60309416/1599699
Андрій,

53

У більшості сучасних браузерів існує примітивний тип даних для символів, який може бути використаний для створення перерахунку. Це забезпечить безпеку типу enum, оскільки кожне значення символу гарантується JavaScript унікальним, тобтоSymbol() != Symbol() . Наприклад:

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

Щоб спростити налагодження, ви можете додати опис до значень перерахувань:

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

Демонстрація Plunker

На 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
Мех ... Значення перерахунків часто потрібно бути серіалізаційними, а символи не настільки зручні для серіалізації та десеріалізації.
Енді,

3
Це тільки я, або це Object.freezeлише для людей, які не сприйняли факту, що «мавпа на власний ризик» є соціальним договором JS?
Енді

@Andy так серіалізація дратує. Я в кінцевому підсумку робить явним toJSONна вміщає класу використовувати цей підхід: stackoverflow.com/questions/58499828 / ...
Чіро Сантіллі冠状病毒审查六四事件法轮功

30

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

Давайте вирішимо проблему: розмір файлу. Кожна інша відповідь, перерахована тут, роздуває ваш край до кінця. Представляю вам, що для найкращої продуктивності, читабельності коду, управління масштабними проектами, натяку на синтаксис у багатьох редакторах коду та зменшення розміру коду шляхом мінімізації це правильний спосіб зробити перерахування: змінні підкреслення-позначення.


wvwvwvwvwwwwwwwwwwwwwwwwwwwwwwwwwwwvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww

Змінні підкреслення

Як показано на графіку вище та прикладі нижче, ось п’ять простих кроків для початку:

  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 wvwvwww wvwvw wvwvwv vwvxwvw wvwvww wvwwvw wvwxxww

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

Крім того, цей синтаксис перерахунків дозволяє чітко і стисло розширювати клас, як показано нижче. Щоб розширити клас, додайте збільшуючий номер до 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 байт)

Деякі можуть сказати, що це менш практично, ніж інші рішення: воно забирає багато тонн місця, писати потрібно багато часу, і воно не покрите синтаксисом цукру. Ці люди були б праві, якщо вони не зміщують свій код. Однак жодна розумна людина не залишить незмінений код у кінцевому продукті. Для цього вдосконалення компілятор закриття - найкраще, що я мав ще знайти. Доступ до Інтернету можна знайти тут . Компілятор закриття може взяти всі ці дані перерахування та ввести їх, зробивши ваш Javascript дуже малим та запустити супер-пупер швидко. Таким чином, Minify with Closure Compiler. Поспостерігайте.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwww wvwvw wvwvwv vwvxwvw wvwvww wvwwvw wvwxxww

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

Компілятор закриття може виконати кілька неймовірних оптимізацій за допомогою висновків, що виходять за рамки можливостей будь-якого іншого міні-JavaScript Javascript. Компілятор закриття може вбудовувати примітивні змінні, встановлені на фіксоване значення. Компілятор закриття також може робити висновки на основі цих вкладених значень та виключати невикористані блоки в if-операторах та циклі.

Введення коду через компілятор закриття

'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 байт)

Компілятор закриття винагороджує вас за кодування розумнішого та добре впорядкованого коду, оскільки, хоча багато мініфієри карають організований код більшим мінімізованим розміром файлу, компілятор закриття здатний просіяти всю вашу чистоту та розумність, щоб вивести ще менший розмір файлу, якщо ви використовуєте трюки як перерахування імен змінних. Це, з урахуванням цього, святий грааль кодування: інструмент, який одночасно допомагає вашому коду з меншим мінімізованим розміром і допомагає вашому розуму, навчаючи кращих навичок програмування.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwww wvwvw wvwvwv vwvxwvw wvwvww wvwwvw wvwxxww

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

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

Джерело без використання перерахунків (довжина: 1,973 байт (477 байт коротше перерахованого коду!))
Мінімізовано без використання перерахунків (довжина: 843 байти (238 байт довше перерахованого коду ))

Діаграма розмірів коду



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


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwww wvwvw wvwvwv vwvxwvw wvwvww wvwwvw wvwxxww

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

Ще одна перевага щодо цієї форми перерахування полягає в тому, що з її допомогою можна легко керувати масштабними проектами без шкоди для мінімізованого розміру коду. Працюючи над великим проектом з великою кількістю інших людей, може бути корисним чітко позначити і позначити імена змінних, хто створив код, щоб оригінальний творець коду був швидко ідентифікований для спільного виправлення помилок.

// 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";

Можна заперечувати проти коду з перерахуваннями, які здаються набагато довшими, ніж код із звичайними об'єктами, але зовнішній вигляд може обманювати. Важливо пам’ятати, що розмір вихідного коду не пропорційний розміру виводу при використанні компілятора епічного закриття. Поспостерігайте.

/// 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 wvwvwww wvwvw wvwvwv vwvxwvw wvwvww wvwwvw wvwxxww

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

Крім того, ця особиста вишня у верхній частині використовує цю форму перерахування разом із текстовим редактором CodeMirror у режимі Javascript. Режим підсвічування синтаксису JavaScript CodeMirror виділяє локальні змінні в поточній області. Таким чином, ви моментально знаєте, коли правильно вводити ім'я змінної, тому що якщо ім'я змінної раніше було оголошено заvar ключового слова, то ім'я змінної набуває особливого кольору (cyan за замовчуванням). Навіть якщо ви не використовуєте 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 wvwvwww wvwvw wvwvwv vwvxwvw wvwvww wvwwvw wvwxxww

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Я думаю, що можна сказати, що ця методологія перерахування - це найкращий спосіб не лише для мінімізованого розміру коду, але й для продуктивності, налагодження та співпраці.

Прочитавши корисне запитання, я дякую авторові за те, що він вклав час у їх написання, натиснувши верхню ліву стрілку вгору у полі запитання. У кожному полі відповідей також є одна із цих стрілок вгору.


Е-е. Я дуже віддаю перевагу читабельності та простоті використання та розуміння розміру коду.
Андрій

1
@Andrew З моєю відповіддю ви можете мати і те, і інше. Моя відповідь дає найпростіший у використанні / керуванні код та найменший розмір мінімізованого коду.🙂
Джек Гіффін,

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

1
@Andrew… YEA перешкоджає можливості компілятора закриття можливості мінімізувати. Порівняйте вихідний код з YEA (3549 байт) з мінімізованим кодом з YEA (1344 байт) з мінімізованим кодом з моїм рішенням (604 байти). Нарешті, YEA передбачає "відображення по імені", оскільки воно відокремлює імена рядків від перелічених ідентифікаторів. Шахта вважає лише ідентифікатор, тому не потрібно «відображення по імені», що призводить до спрощення дизайну та кращої продуктивності. Дякуємо за те, що ви поділилися своїм рішенням, але для його практичного використання потрібно багато виправлень.
Джек Гіффін

1
@Andrew Ви маєте право на свою думку, як і я, mine
Джек Гіффін,

23

Я пограв із цим, як люблю свої переписки. =)

Використовуючи, Object.definePropertyя думаю, що я придумав дещо життєздатне рішення.

Ось jsfiddle: http://jsfiddle.net/ZV4A6/

Використовуючи цей метод .., ви повинні (теоретично) мати можливість викликати та визначати значення enum для будь-якого об'єкта, не впливаючи на інші атрибути цього об’єкта.

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 змішаний = mkenum ('ЧЕРНИЙ', {RED: 0x0F00, BLUE: 0X0F, GREEN: 0x0F0, WHITE: 0x0FFF, ONE: 1}, TWO, THREE, FOUR) ; // Додавання мого коду як відповідь нижче. Дякую.
Ендрю Філіпс

Якщо чесно, я вже навіть цим не користуюся. Я використовую компілятор закриття Google, і це працює не надто добре (або це просто ускладнює), якщо ви використовуєте налаштування «Розширений». Тому я щойно повернувся до стандартних позначень об'єктів.
Дункан

1
falseє типовим для writable, enumerableі configurable. Не потрібно жувати за замовчуванням.
припинення

23

Використовуйте JavaScript- проксі

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, яку ви отримуєте від традиційних мов, є те, що вони вибухають (викидають помилку під час компіляції), якщо ви намагаєтесь отримати доступ до перелічувача, який не існує.

Окрім заморожування макетної структури перерахунку, щоб запобігти випадковому / зловмисному введенню додаткових значень, ніхто з інших не відповідає на властиву особливість 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"


ВІДПОВІДЬ: Який чорт проксі?

Я пам’ятаю, коли я вперше почав бачити слово proxy скрізь, це точно не мало сенсу для мене тривалий час. Якщо це зараз ви, я думаю, що простий спосіб узагальнити проксі - це вважати їх програмними засобами, установами чи навіть людьми, які виступають посередниками або посередниками між двома серверами, компаніями чи людьми.


Як зробити щось на зразок myEnum.valueOf ("someStringValue")? Очікується: якщо рядок введення має значення елемента перелічувача, слід повернути елемент. Якщо жоден елемент не має цього рядкового значення, киньте виняток.
sscarduzio

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

Мій enum - const logLevelEnum = new Enum ({INFO: "info", 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]яке повертає "Бар", незалежно від порядку декларації.


1
Плюс MyEnum ["Бар" працює, який повертає 1 ... <3 TypeScript поки що ...
Девід Карлаш

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
Чи можете ви пояснити, чому мати батьківський клас є перевагою? Я відчуваю, що мені чогось не вистачає!
Джон G

7
Не робіть цього. new ColorEnum()абсолютно не має сенсу.
Бергі

3
продовження перерахунку звучить божевільно, насправді
Codii

як тільки мова не підтримує її по-домашньому, має сенс дотримуватися цієї конвенції та використовувати її так! я згоден!
xpto

Я думаю (?), На що потрапляє ОП, це: Перевага чистого статичного полягає в тому, що він доступний скрізь як сингл, і вам не потрібно інстанціювати клас - ОП не пропонує цього робити! Я думаю , що він говорить, що суперклас 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
});

Зазвичай я використовую останні два методи для відображення перерахунків з об’єктів повідомлень.

Деякі переваги цього підходу:

  • Легко оголосити перерахунки
  • Легкий доступ до ваших друзів
  • Ваші перерахунки можуть бути складних типів
  • Клас Enum має деяке асоціативне кешування, якщо ви багато використовуєте getByValue

Деякі недоліки:

  • Деяке безладне управління пам’яттю відбувається там, коли я зберігаю посилання на перерахунки
  • Досі немає безпеки безпеки

14

Створіть об'єкт буквально:

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

12
constне робить властивості об'єкта незмінними, це лише означає, що змінна Modesне може бути переназначена на щось інше. Щоб зробити його більш повним, використовуйте Object.freeze()поряд const.
rvighne

Будь ласка, не використовуйте Object.freeze. Це заважає компілятору закриття не вбудовувати об'єкт.
Джек Гіффін

11

Якщо ви використовуєте Backbone , ви можете безкоштовно отримати функцію перерахунку (знайти за id, ім’я, користувацькі члени) безкоштовно, використовуючи 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

Я змінив рішення Андре "Фі":

  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тепер посилається на Daysenum:

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

Гарна думка! Зроблю!
Vivin Paliath

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;

Порівняння:

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, що використовуються в цьому прикладі, мають звичайний еквівалент 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, який може отримувати значення 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 у 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), цікаво, що ви можете використати наведене нижче з 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 " 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 шлях, (ітератор, заморожування), використання:

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', але потім зрозумів, що його мертвий простий - оператор Assignment присвоює значення, а потім повертає його:

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 року - ось ось сучасне рішення. Я пишу рішення як модуль вузла, і включив тест з використанням Mocha і Chai, а також підкреслити JS. Ви можете легко проігнорувати ці дані та просто взяти код 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.keys, а самі ключі можна зіставити з цілими константами. І ви можете повторно використовувати завод з різними значеннями та експортувати створені Enums за допомогою модульного підходу Node.

Знову ж таки, якщо ви просто випадковий користувач або в браузері тощо, просто візьміть заводську частину коду, потенційно видаливши бібліотеку підкреслення, якщо ви не бажаєте використовувати її у своєму коді.


5
Чи можете ви опублікувати відповідь просто "ось як це зробити як випадковий користувач, який просто хоче переліків, а не фабрик, підкреслень чи чогось фантазії"?
GreenAsJade

5
Незважаючи на те, що це досить дивовижно з точки зору розробників, воно не дуже чисте і не читабельне. Рішення Enum з ОП легше і легше читати, а тому краще використовувати. І все-таки досить дивовижно, що ви придумали це.
Девід

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.