Питання Який бажаний синтаксис для визначення енуму в JavaScript?


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

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

// later on

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

Чи є ще кращий ідіом?


1678
2017-11-13 19:09


походження


Я взяв тест на Javascript 1.8 на odesk, і вони запитали, що має вирішити перерахування в javascript, 2 очевидних відповіді були або ключем, або генератором. Я неясно пам'ятаю enums у курсі c + + і дійсно не знаю, що є генераторами; але потенційно це допомагає. - Devin G Rhode
Не використовуйте 0 як номер переліку. Якщо це не використовується для того, що не було встановлено. JS лікує false || undefined || null || 0 || "" || '' || NaN все як однакове значення при порівнянні використання ==. - matsko
@matsko - це не просто аргумент проти використання ==? - sdm350
Але false == 0 і +null == 0 (і переходи до цифр відбуваються іноді, коли ви цього не очікуєте), в той час як null == undefined теж і +undefined є NaN (хоча NaN != NaN) - sanderd17
Матриця подвійної рівності є більш заплутаною, ніж автоматичне форматування Microsoft Word - aaaaaa


Відповіді:


З 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 enums.

Однак це не заважає вам призначити небажане значення для змінної, що часто є головною метою енусів:

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

Один із способів забезпечення більш високого ступеню безпеки типу (з пріоритетами або іншим способом) - це використовувати інструмент типу TypeScript або Потік.

Джерело

Цитати не потрібні, але я зберігав їх для послідовності.


541
2018-02-18 11:03



Згідно Вікіпедії (en.wikipedia.org/wiki/JavaScript#Versions) це стосується Firefox 4, IE 9, Opera 11.60, і я знаю, що він працює в Chrome. - Artur Czajka
Це правильна відповідь зараз у 2012 році. Більш простий: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Вам не потрібно вказувати ідентифікатор, ви можете просто використовувати порожній об'єкт для порівняння енусів. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday - Gabriel Llamas
Для зворотної сумісності if (Object.freeze) { Object.freeze(DaysEnum); } - saluce
Я хотів би зазначити, що робити ({ monday: {},  тощо, якщо ви перетворите цей об'єкт у JSON через строгість, яку ви отримаєте [{"day": {}}] який не буде працювати. - jcollum
@Supuhstar Моя думка про це питання зараз відрізняється. Не використовуйте "freeze" (), це абсолютно марно, а трата часу робити "дурні" речі. Якщо ви хочете показати перелік, просто виділіть це: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Порівняння об'єктів, подібних до мого попереднього коментаря, набагато краще, ніж порівняння чисел. - Gabriel Llamas


Це не так багато відповіді, але я б сказав, що це працює чудово, особисто

Сказавши це, тому що не важливо, які значення (ви використали 0, 1, 2), я б використовувати значущий рядок у випадку, коли ви хотіли вивести поточне значення.


583
2017-11-13 19:13



Ось проста фабрика переходів, яку я створив; npmjs.org/package/simple-enum - Tolga E
На жаль, але для реалізації enum є кілька проблем. Я рекомендую використовувати це замість: github.com/rauschma/enums - paldepind
Про це сказано в іншому відповіддю, але оскільки ця відповідь є прийнятою відповіддю, я опублікую це тут. Рішення ОР є правильним. Це буде навіть краще, хоча, якщо використовувати з Object.freeze(). Це не дозволить іншому коду змінювати значення enum. Приклад: var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2}); - Sildoreth
@Tolgae спасибі за цю бібліотеку! Це надихнуло мене не тільки звести його до мінімуму, але і додати кілька особливостей! Я піднімаю тебе і поклав все це тут: github.com/BlueHuskyStudios/Micro-JS-Enum - Supuhstar
@Supuhstar Це здорово! Я радий, що можете використовувати його .. Не соромтеся зробити запит тягнути, якщо ви хочете його об'єднати в цій бібліотеці, то я можу оновити бібліотеку npm - Tolga E


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


Попередження назви вже можливо:

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

Крім того, ви можете зробити значення об'єктів, так що ви можете мати пиріг і їсти його теж:

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

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

У Javascript, оскільки це динамічна мова, можна навіть додавати значення enum до набору пізніше:

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

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

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

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

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

Е.Г .:

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


477
2018-03-04 22:31



так, як би ти пішов і створив просто SIZE, якщо у вас є тільки його ім'я? - Johanisma
@ Джоханізма: Цей варіант використання не має сенсу для енусів, оскільки ціла ідея їх полягає в тому, що ви знаєте всі цінності заздалегідь. Проте немає нічого, що завадить вам додавати додаткові значення пізніше в Javascript. Я додам приклад цього до моєї відповіді. - Stijn de Witt
Дуже добре відповіли. Я випадковий користувач JS / JQ, коли це потрібно, і це допомогло ситуації з SharePoint, які потребували вирішення. - GoldBishop
+1 для посилання на ваш пост із підходом до властивостей. Елегантний в тому, що основні декларації є простими, як у OP, при наявності бажаних додаткових властивостей. - goodeye
@DavideAndrea Ви маєте рацію. З тих пір я почав розміщувати свій блог під власним доменним ім'ям. Оновлено посилання. Дякуємо, що згадали це! - Stijn de Witt


Підсумок: Ви не можете.

Ви можете підробити це, але ви не отримаєте тип безпеки. Як правило, це робиться шляхом створення простого словника зі значеннями рядків, наведеними у цілі значення. Наприклад:

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

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


79
2017-08-21 20:56



чи є безпека типу в javascript? - Scott Evernden
Тому не майте значення для властивостей об'єкта. Використовуйте getter для доступу до переліку (зберігається як властивість, скажімо, "приватного" об'єкта). Наївна реалізація виглядатиме так - var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1 - kangax
@ Шотт Евернден: пункт прийнято. @kangax: справа в тому, що це все-таки хак. Емави просто не існують у Javascript, період, кінець історії. Навіть модель, запропонована Тімом Силвестром, все ще є менш ідеальним злом. - Randolpho
Посипання кодом літералами не дуже ремонтопридатне, тому має сенс створювати для нього константи. Звичайно, Javascript також не має констант. Тому в основному це лише спосіб написати чистий код. Це не може бути виконане, але не так багато в Javascript може. Ви можете повторно визначити константи або функції, або в основному щось. EG: document.getElementById = function () {оповіщення ("Ви накручені, Javascript не є типовим".);}; - Stijn de Witt
@Randolpho: Як щодо Object.freeze Артура Чайки? Хіба це не означає, що робота перешкоджає вам встановлювати понеділок до четверга? - Michael


Ось що ми всі хочемо:

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');

46
2017-07-13 00:28





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

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

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

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

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

На GitHub ви можете знайти обгортку, яка спрощує код, необхідний для ініціалізації переліку:

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

41
2018-05-05 16:32



Це теоретично правильна відповідь. На практиці підтримка браузера до 2015 року далеко не достатня. Не виробництво готові далеко. - vbraun
Хоча підтримки браузера ще немає, це найкраща відповідь, оскільки це близько до чого Symbol призначений для. - rvighne
Значення enum часто повинні бути серіалізуються, і Символи не настільки зручні для серіалізації та десеріалізації. - Andy


Я граю з цим, тому що я люблю свої енуми. =)

Використовуючи 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

23
2017-08-21 10:34



Якщо ви додаєте return this; в кінці Enum ви могли б зробити: var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW'); - HBP
Я не вважав це, оскільки це не моє звичайне спосіб робити речі. Але ви абсолютно правильно! Я буду редагувати це в - Duncan
Мені це дуже подобається, хоча я не великий шанувальник розгортання об'єкта (з глобальною функцією ENUM). Перетворив це на функцію mkenum і додав додаткові числові присвоєння => var mixedUp = mkenum ('BLACK', {RED: 0x0F00, BLUE: 0X0F, GREEN: 0x0F0, WHITE: 0x0FFF, ONE: 1}, ДВА, ТРИ, ЧЕТВЕРТА) ; // Додавання мого коду як відповіді нижче. Дякую. - Andrew Philips
Чесно кажучи, я більше не використовую це. Я використав компілятор закриття Google, і це не працює надто добре (або це просто ускладнює ситуацію), якщо ви використовуєте параметри Додатково. Тому я просто повернувся до стандартного позначення об'єкта. - Duncan
false за замовчуванням для writable, enumerable і configurable. Не потрібно жувати за замовчуванням. - ceving


Це старий, який я знаю, але спосіб, з якого він був реалізований через інтерфейс 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] який повертає "Бар" незалежно від порядку декларації.


17
2018-06-24 16:11



Плюс MyEnum ["Bar"] працює, що повертає 1 ... <3 TypeScript до цих пір ... - David Karlaš
і, звичайно, якщо ви насправді використовуєте Typescript: enum MyEnum { Foo, Bar, Foobar } - parliament