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

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

// later on

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

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

2193
David Citron 13 Ноя 2008 в 22:09

27 ответов

Лучший ответ

Начиная с версии 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.

Источник

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

859
Ninjakannon 4 Окт 2019 в 12:05

ОБНОВИТЬ

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


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

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

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

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

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

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

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

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

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

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

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

Примере

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

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

500
informatik01 4 Ноя 2019 в 16:32

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

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

606
Gareth 13 Ноя 2008 в 19:22

Я предложил этот подход, который смоделирован после перечислений в 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
Vivin Paliath 18 Сен 2015 в 22:11

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

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

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

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

Plunker demo

На 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
52
YakovL 31 Дек 2017 в 23:33

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

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

Тогда

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

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

 class ColorEnum  extends Enum {/*....*/}
15
Abdennour TOUMI 6 Янв 2017 в 07:07

Я только что опубликовал пакет 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, строковые константы, генерация растровых изображений и т. д. Оформить заказ package / constjs для получения дополнительной информации

Чтобы перейти с gen_enum на constjs, просто измените оператор

var genEnum = require('gen_enum');

Кому

var genEnum = require('constjs').enum;
4
Gelin Luo 1 Май 2015 в 23:44

Вот как 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
Julius Dzidzevičius 3 Май 2018 в 18:44

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

Создайте

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
Ilya Gazman 17 Авг 2016 в 12:38

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

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

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

  • Там происходит некоторое грязное управление памятью, так как я храню ссылки на перечисления
  • Все еще нет типа безопасности
15
Chris 15 Май 2012 в 09:31

Это старая версия, которую я знаю, но с тех пор она была реализована через интерфейс 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
Rob Hardy 24 Июн 2013 в 16:11

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

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


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwv

Underscore-Notation Variables

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

  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 (например, {{X7} } ) . За все мои годы использования === у меня никогда не было проблем с использованием 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. Может быть сложнее и глючнее добавить новую запись в другие системы перечисления.


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw wvwvw wvwvwv vwvwvw wvwvvw wvwwvw xwvwvwvw wvwvw

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

Кроме того, этот синтаксис перечислений допускает ясное и краткое расширение классов, как показано ниже. Чтобы расширить класс, добавьте инкрементный номер к записи 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_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

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

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


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw wvwvw wvwvwv vwvwvw wvwvvw wvwwvw xwvwvwvw wvwvw

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

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

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

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


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw wvwvw wvwvwv vwvwvw wvwvvw wvwwvw xwvwvwvw wvwvw

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

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

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

Chart of code sizes



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


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw wvwvw wvwvwv vwvwvw wvwvvw wvwwvw xwvwvwvw wvwvw

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

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

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

Sub 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

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

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

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

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

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

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

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

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

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

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

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


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw wvwvw wvwvwv vwvwvw wvwvvw wvwwvw xwvwvwvw wvwvw

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

Кроме того, личная вишня сверху использует эту форму перечислений вместе с CodeMirror текстовый редактор в режиме Javascript. Режим подсветки синтаксиса Javascript в CodeMirror выделяет локальные переменные в текущей области видимости. Таким образом, вы сразу узнаете, когда правильно вводите имя переменной, потому что, если имя переменной было ранее объявлено с ключевым словом 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
}

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


< SUP > < SUP > < SUP > < SUP > < SUP > < SUP > < я > wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvxwvw wvwvw wvwvwv vwvwvw wvwvvw wvwwvw xwvwvwvw wvwvw

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

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

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

27
Jack Giffin 24 Мар 2020 в 00:07

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
Joseph Merdrignac 14 Фев 2018 в 23:44

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

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

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

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

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

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

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

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

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

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

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

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

var val2 = enum1.item2;

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

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

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

var arr = enum1.GetObjArr();

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

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

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

var html = enum1.GetSelectOptionsHTML();

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

"<option value='0'>item1</option>..."
4
Tim Kara 8 Дек 2016 в 18:22

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

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

console.log(Colors.WHITE)  //this prints out "0"
3
user2254487 6 Окт 2013 в 22:07

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

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

Файл: enums.js

_ = require('underscore');

var _Enum = function () {

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

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

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

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

Файл: enumsSpec.js

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


describe('enums', function () {

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

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

});

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

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

3
arcseldon 20 Ноя 2014 в 17:44

Это легко использовать, я думаю. 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).

var Helper = {
    isEmpty: function (obj) {
        return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
    },

    isObject: function (obj) {
        return (typeof obj === 'object');
    },

    sortObjectKeys: function (object) {
        return Object.keys(object)
            .sort(function (a, b) {
                c = a - b;
                return c
            });
    },
    containsItem: function (arr, item) {
        if (arr && Array.isArray(arr)) {
            return arr.indexOf(item) > -1;
        } else {
            return arr === item;
        }
    },

    pushArray: function (arr1, arr2) {
        if (arr1 && arr2 && Array.isArray(arr1)) {
            arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
        }
    }
};
function TypeHelper() {
    var _types = arguments[0],
        _defTypeIndex = 0,
        _currentType,
        _value,
        _allKeys = Helper.sortObjectKeys(_types);

    if (arguments.length == 2) {
        _defTypeIndex = arguments[1];
    }

    Object.defineProperties(this, {
        Key: {
            get: function () {
                return _currentType;
            },
            set: function (val) {
                _currentType.setType(val, true);
            },
            enumerable: true
        },
        Value: {
            get: function () {
                return _types[_currentType];
            },
            set: function (val) {
                _value.setType(val, false);
            },
            enumerable: true
        }
    });
    this.getAsList = function (keys) {
        var list = [];
        _allKeys.forEach(function (key, idx, array) {
            if (key && _types[key]) {

                if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                    var json = {};
                    json.Key = key;
                    json.Value = _types[key];
                    Helper.pushArray(list, json);
                }
            }
        });
        return list;
    };

    this.setType = function (value, isKey) {
        if (!Helper.isEmpty(value)) {
            Object.keys(_types).forEach(function (key, idx, array) {
                if (Helper.isObject(value)) {
                    if (value && value.Key == key) {
                        _currentType = key;
                    }
                } else if (isKey) {
                    if (value && value.toString() == key.toString()) {
                        _currentType = key;
                    }
                } else if (value && value.toString() == _types[key]) {
                    _currentType = key;
                }
            });
        } else {
            this.setDefaultType();
        }
        return isKey ? _types[_currentType] : _currentType;
    };

    this.setTypeByIndex = function (index) {
        for (var i = 0; i < _allKeys.length; i++) {
            if (index === i) {
                _currentType = _allKeys[index];
                break;
            }
        }
    };

    this.setDefaultType = function () {
        this.setTypeByIndex(_defTypeIndex);
    };

    this.setDefaultType();
}

var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var enumA = new TypeHelper(TypeA, 4);

document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setType("200L", false);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

enumA.setDefaultType();
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setTypeByIndex(1);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

document.writeln("is equals = ", (enumA.Value == TypeA["2"]));
3
Community 23 Май 2017 в 11:47
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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

5
Shivanshu Goyal 29 Янв 2015 в 01:32

Если вы используете Backbone, вы можете получить полнофункциональную функцию enum (поиск по идентификатору, имени, пользовательским элементам) бесплатно используя 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
Yaroslav 24 Апр 2012 в 14:04

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

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

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

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

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


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

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

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

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

// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue
4
Blake Bowen 17 Мар 2015 в 21:24

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

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

4
LNT 26 Дек 2018 в 06:11

Я изменил решение Андре '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
David Miró 12 Апр 2013 в 17:04

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

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

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

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

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

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

< Сильный > Edit

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

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

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

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

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

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

83
Randolpho 6 Июн 2012 в 16:09

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

Помимо замораживания поддельной структуры 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"

В стороне: какого черта прокси?

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

21
j2k 12 Фев 2019 в 17:31

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

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
14
hvdd 1 Июн 2017 в 17:03

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

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
Xeltor 15 Май 2014 в 04:20

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

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

Делая это, к константам можно получить доступ обычным способом (Да Нет. ДА, Color.GREEN), и они получают последовательное значение типа int (НЕТ = 0, ДА = 1; КРАСНЫЙ = 0, ЗЕЛЕНЫЙ = 1, СИНИЙ = 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
Andre 'Fi' 19 Июл 2011 в 01:21