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

1728

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

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

// later on

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

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

  • 106
    Не используйте 0 в качестве номера перечисления. Если это не используется для чего-то, что не было установлено. JS рассматривает false || undefined || null || 0 || "" || '' || NaN все как то же значение при сравнении с использованием == .
  • 118
    @matsko это не просто аргумент против использования ==?
Показать ещё 11 комментариев
Теги:
enums
syntax

46 ответов

698
Лучший ответ

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

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

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

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

Источник

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

  • 6
    Согласно Википедии ( en.wikipedia.org/wiki/JavaScript#Versions ) это применимо к Firefox 4, IE 9, Opera 11.60, и я знаю, что это работает в Chrome.
  • 72
    Это правильный ответ сейчас, в 2012 году. Более просто: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... }); , Вам не нужно указывать идентификатор, вы можете просто использовать пустой объект для сравнения перечислений. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Показать ещё 23 комментария
592

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

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

  • 372
    Об этом говорилось в другом ответе, но так как этот ответ является принятым ответом, я опубликую его здесь. Решение ОП является правильным. Однако будет еще лучше, если использовать его с Object.freeze() . Это не позволит другому коду изменять значения перечисления. Пример: var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
  • 5
    @TolgaE спасибо за эту библиотеку! Это вдохновило меня не только свести его к минимуму, но и добавить пару функций! Я раздвоил твой и выложил все это здесь: github.com/BlueHuskyStudios/Micro-JS-Enum
Показать ещё 4 комментария
508

ОБНОВЛЕНИЕ. Спасибо всем за все, что мне нравится, но я не думаю, что мой ответ ниже - лучший способ написать перечисления в Javascript. См. Мой блог для более подробной информации: Перечисления в Javascript.


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

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

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

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

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

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

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

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

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

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

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

например:

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

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

  • 0
    Итак, как бы вы пошли и создали просто РАЗМЕР, если у вас есть только его имя?
  • 2
    @Johanisma: этот вариант использования не имеет смысла для перечислений, так как вся идея в том, что вы знаете все значения заранее. Однако ничто не мешает вам добавить дополнительные значения позже в Javascript. Я добавлю пример этого в мой ответ.
Показать ещё 5 комментариев
80

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

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

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

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

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

Здесь мы все хотим:

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

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

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

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

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

Plunker demo

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

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

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE
  • 0
    Это правильный ответ в теории. На практике поддержка браузеров 2015 года далеко не достаточна. Не готово к производству.
  • 0
    Хотя поддержки браузера пока нет, это лучший ответ, поскольку он близок к тому, для чего предназначен Symbol .
Показать ещё 2 комментария
23

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

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

Здесь jsfiddle: http://jsfiddle.net/ZV4A6/

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

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

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

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

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3
  • 3
    Если вы добавите, return this; в конце Enum вы можете сделать: var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
  • 0
    Я не учел это, потому что это не мой обычный метод ведения дел. Но ты абсолютно прав! Я отредактирую это в.
Показать ещё 3 комментария
18

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

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

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

  • 1
    Плюс работает MyEnum ["Bar"], который пока возвращает 1 ... <3 TypeScript ...
  • 3
    и, конечно, если вы действительно используете Typescript: enum MyEnum { Foo, Bar, Foobar }
16

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

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

// later on

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

Кроме того, этот синтаксис допускает ясное и краткое расширение классов, как показано ниже.

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

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

    const ENUM_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          ENUMLEN_PIXELCOLOR   = 1,
          ENUM_SOLIDCOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_SOLIDCOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_SOLIDCOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUMLEN_SOLIDCOLOR   = ENUMLEN_PIXELCOLOR+3,
          ENUM_ALPHACOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_ALPHACOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_ALPHACOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUM_ALPHACOLOR_A    = ENUMLEN_PIXELCOLOR+3,
          ENUMLEN_ALPHACOLOR   = ENUMLEN_PIXELCOLOR+4,
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(rawstr) {
        rawstr = rawstr.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

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

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

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

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

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

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

15

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

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

то

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

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

 class ColorEnum  extends Enum {/*....*/}
  • 3
    Не могли бы вы объяснить, почему наличие родительского класса является преимуществом, пожалуйста? Я чувствую, что что-то упустил!
  • 6
    Не делай этого. new ColorEnum() имеет абсолютно никакого смысла.
Показать ещё 2 комментария
15

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Некоторое грязное управление памятью происходит там, так как я сохраняю ссылки на перечисления
  • По-прежнему нет безопасности типа
13

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

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

class Enum {
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (target[name]) {
          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 (target[name]) {
          return target[name]
        }
        throw new Error('No such enumerator: ${name}')
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    }


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


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

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

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

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

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

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

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

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

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
  • 11
    const не делает свойства объекта неизменяемыми, это лишь означает, что переменная Modes не может быть переназначена чему-либо другому. Чтобы сделать его более полным, используйте Object.freeze() вместе с const .
  • 0
    Пожалуйста, не используйте Object.freeze . Это препятствует тому, чтобы Закрытие Компилятора встраивало объект.
11

Если вы используете Backbone, вы можете бесплатно получить полнофункциональную функцию перечисления (найти по id, name, custom members), используя Backbone.Collection.

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

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

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

// using
Colors.Red.print()
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
  • 0
    @JackGiffin Я согласен, что ваш ответ более производительный и мой может занять больше памяти, хотя вы не должны предполагать, что все хотят перечисления, как это реализовано в C ++. Пожалуйста, уважайте другие ответы и разработчиков, которые могут предпочесть этот, а не ваш.
7

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

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

Тест:

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

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

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

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

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

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

или даже это:

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

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

Назначение:

var wildType = CONST_WILD_TYPES.REGULAR;

Comparision:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}
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 "перечислению", а не к некоторому другому перечислению (что было бы невозможно отличить, если вы использовали общие объекты или символы).

Обновление

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

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

    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;
    
  • 0
    Хороший пример :-)
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

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 in С#):

var val2 = enum1.item2;

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

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

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

var arr = enum1.GetObjArr();

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

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

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

var html = enum1.GetSelectOptionsHTML();

Что имеет место:

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

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

})();
  • 0
    Это выглядит красиво, может быть, вам следует проверить наличие метода freeze для обратной совместимости? Например, if (Object.freeze) { Object.freeze(values); }
  • 0
    Хорошая точка зрения! Сделаю!
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 

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

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

Обновление

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

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

var genEnum = require('gen_enum');

к

var genEnum = require('constjs').enum;
4
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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

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

Вот как 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
3

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

Создать

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

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

console.log(Status.Ready) // 1

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

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

Он прост в использовании, я думаю. https://stackoverflow.com/questions/5467129/sort-javascript-object-by-key

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

UPDATE:

Есть мои вспомогательные коды (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

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

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

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

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

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


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

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

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

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

// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue
  • 0
    очень умно спасибо
3

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

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

Файл: enums.js

_ = require('underscore');

var _Enum = function () {

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

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

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

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

file: enumsSpec.js

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


describe('enums', function () {

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

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

});

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

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

  • 5
    Не могли бы вы опубликовать ответ, просто "вот как это сделать для обычного пользователя, который просто хочет перечисления, а не фабрики, подчеркивания или что-то необычное"?
  • 5
    Несмотря на то, что это довольно круто с точки зрения разработчиков, оно не очень чистое и не читаемое. Решение Enum из OP проще и удобнее во всех отношениях, и поэтому лучше в использовании. Тем не менее, довольно круто, что вы придумали это.
3

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

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

console.log(Colors.WHITE)  //this prints out "0"
  • 5
    Эта функция не нужна и дает вам тот же результат, что и ОП.
2

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(', ') }

}
2

Вы можете попробовать следующее:

   var Enum = Object.freeze({
            Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
            Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
            });

    alert(Enum.Role.Supervisor);
    alert(Enum.Color.GREEN);
    var currentColor=0;
    if(currentColor == Enum.Color.RED) {
       alert('Its Red');
    }
2

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

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


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

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

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

  • 0
    Это не совсем перечисление. Но я вижу, что это очень полезно.
  • 0
    @ Как бы ты их назвал ^^? AugmentedEnum ?
Показать ещё 2 комментария
2

Я сделал это некоторое время назад, используя смесь __defineGetter__ и __defineSetter__ или defineProperty в зависимости от версии JS.

Здесь функция генерации перечисления, которую я сделал: https://gist.github.com/gfarrell/6716853

Вы бы использовали его следующим образом:

var Colours = Enum('RED', 'GREEN', 'BLUE');

И он создаст неизменяемую строку: int dictionary (enum).

  • 0
    Хорошо, я переписал его, и теперь он не зависит от библиотеки (хотя он предполагает какой-то загрузчик AMD, но его можно удалить).
1
class Enum {
  constructor (...vals) {
    vals.forEach( (val, i) => {
      Object.defineProperty(this, val.toUpperCase(), {
        get () {
          return i;
        },
        set (val) {
          const enum_val = "CONSTANT";
          // generate TypeError associated with attempting to change the value of a constant
          enum_val = val;
        }
      });
    });
  }
}

Пример использования:

const COLORS = new Enum("red", "blue", "green")
console.log(COLORS.RED) // 0
console.log(COLORS.GREEN) // 2
  • 1
    Я думаю, что вам не хватает создания Enum в вашем примере
  • 0
    черт, это сбивало с толку :)
1

В TypeScript вы можете написать:

const enum COLORS { 
    RED, 
    GREEN, 
    YELLOW 
};
console.log(COLORS.GREEN);  // 1
1

Что такое перечисление в выражении my: это неизменный объект, который всегда доступен, и вы можете сравнивать элементы с eachother, но элементы имеют общие свойства/методы, но сами объекты или значения не могут быть изменены, и они создаются только один раз.

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

Итак, для этого вам нужны объекты с одним и тем же экземпляром, чтобы вы могли проверить, является ли это типом перечисления if(something instanceof enum) Также, если вы получаете объект перечисления, который вы хотите сделать с ним, независимо от типа перечисления, он всегда должен отвечать таким же образом.

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

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


Это то, что я использую для Enums. Поскольку перечисления неизменяемы (или должны быть, по крайней мере, хе-хе), я замораживаю объекты, чтобы их нельзя было легко манипулировать.

Перечисления могут использоваться EnumField.STRING, и у них есть свои методы, которые будут работать с их типами. Чтобы проверить, что что-то передано объекту, вы можете использовать if(somevar instanceof EnumFieldSegment)

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

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

/**
 * simple parameter object instantiator
 * @param name
 * @param value
 * @returns
 */
function p(name,value) {
    this.name = name;
    this.value = value;
    return Object.freeze(this);
}
/**
 * EnumFieldSegmentBase
 */
function EnumFieldSegmentBase() {
    this.fieldType = "STRING";
}
function dummyregex() {
}
dummyregex.prototype.test = function(str) {
    if(this.fieldType === "STRING") {
        maxlength = arguments[1];
        return str.length <= maxlength;
    }
    return true;
};

dummyregexposer = new dummyregex();
EnumFieldSegmentBase.prototype.getInputRegex = function() { 
    switch(this.fieldType) {
        case "STRING" :     return dummyregexposer;  
        case "INT":         return /^(\d+)?$/;
        case "DECIMAL2":    return /^\d+(\.\d{1,2}|\d+|\.)?$/;
        case "DECIMAL8":    return /^\d+(\.\d{1,8}|\d+|\.)?$/;
        // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
        case "BOOLEAN":     return dummyregexposer;
    }
};
EnumFieldSegmentBase.prototype.convertToType = function($input) {
    var val = $input;
    switch(this.fieldType) {
        case "STRING" :         val = $input;break;
        case "INT":         val==""? val=0 :val = parseInt($input);break;
        case "DECIMAL2":    if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break;
        case "DECIMAL8":    if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break;
        // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
        case "BOOLEAN":     val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf()))  ;break;
    }
    return val;
};
EnumFieldSegmentBase.prototype.convertToString = function($input) {
    var val = $input;
    switch(this.fieldType) {
        case "STRING":      val = $input;break;
        case "INT":         val = $input+"";break;
        case "DECIMAL2":    val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break;
        case "DECIMAL8":    val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break;
        case "BOOLEAN":     val = $input ? "true" : "false"  ;break;
    }
    return val;
};
EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) {
    var val = false;
    switch(this.fieldType) {
        case "STRING":      val = ($val1===$val2);break;
        case "INT":         val = ($val1===$val2);break;
        case "DECIMAL2":    val = ($val1.comparedTo($val2)===0);break;
        case "DECIMAL8":    val = ($val1.comparedTo($val2)===0);break;
        case "BOOLEAN":     val = ($val1===$val2);break;
    }
    return val;
};

/**
 * EnumFieldSegment is an individual segment in the 
 * EnumField
 * @param $array An array consisting of object p
 */
function EnumFieldSegment() {
    for(c=0;c<arguments.length;c++) {
        if(arguments[c] instanceof p) {
            this[arguments[c].name] = arguments[c].value;
        }
    }
    return Object.freeze(this); 
}
EnumFieldSegment.prototype = new EnumFieldSegmentBase();
EnumFieldSegment.prototype.constructor = EnumFieldSegment;


/**
 * Simple enum to show what type of variable a Field type is.
 * @param STRING
 * @param INT
 * @param DECIMAL2
 * @param DECIMAL8
 * @param BOOLEAN
 * 
 */
EnumField = Object.freeze({STRING:      new EnumFieldSegment(new p("fieldType","STRING")), 
                            INT:        new EnumFieldSegment(new p("fieldType","INT")), 
                            DECIMAL2:   new EnumFieldSegment(new p("fieldType","DECIMAL2")), 
                            DECIMAL8:   new EnumFieldSegment(new p("fieldType","DECIMAL8")), 
                            BOOLEAN:    new EnumFieldSegment(new p("fieldType","BOOLEAN"))});
1

Вы можете использовать Object.prototype.hasOwnProperty()

var findInEnum,
    colorEnum = {
    red : 0,
    green : 1,
    blue : 2
};

// later on

findInEnum = function (enumKey) {
  if (colorEnum.hasOwnProperty(enumKey)) {
    return enumKey+' Value: ' + colorEnum[enumKey]
  }
}

alert(findInEnum("blue"))
1

На самом деле нравится то, что сделал @Duncan, но мне не нравится сбрасывать глобальное пространство функций Object с Enum, поэтому я написал следующее:

function mkenum_1()
{
  var o = new Object();
  var c = -1;
  var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};

  for (i in arguments) {
    var e = arguments[i];
    if ((!!e) & (e.constructor == Object))
      for (j in e)
        f(j, (c=e[j]));
    else
      f(e, ++c);
    }

  return Object.freeze ? Object.freeze(o) : o;
}

var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');

console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);

// Output is:
// MED := 1
// LRG := 100

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

function mkenum_2(seed)
{
    var p = {};

    console.log("Seed := " + seed);

    for (k in seed) {
        var v = seed[k];

        if (v instanceof Array)
            p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
        else
            p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
    }
    seed.properties = p;

    return Object.freeze ? Object.freeze(seed) : seed;
}

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

var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });

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

0

Начиная с ES6, вы не можете создавать Enums как таковые, но у вас может быть более элегантный синтаксис, подходящий для автодополнения кода, по крайней мере, в VSCode:

class MyEnum {
  const A = '1'
  const B = '2'
  const C = '3'
}

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

import { MyEnum } from './my-enum'

console.log(MyEnum.B)

Надеюсь это поможет.

0

Этот ответ является альтернативным подходом для конкретных обстоятельств. Мне нужен был набор констант битовой маски, основанных на под-значениях атрибута (в случаях, когда значением атрибута является массив или список значений). Он охватывает эквивалент нескольких перекрывающихся перечислений.

Я создал класс для хранения и генерации значений битовой маски. Затем я могу использовать значения битовой маски псевдо-константы таким образом, чтобы проверить, например, присутствует ли зеленый в значении RGB:

if (value & Ez.G) {...}

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

class Ez {
constructor() {
    let rgba = ["R", "G", "B", "A"];
    let rgbm = rgba.slice();
    rgbm.push("M");              // for feColorMatrix values attribute
    this.createValues(rgba);
    this.createValues(["H", "S", "L"]);
    this.createValues([rgba, rgbm]);
    this.createValues([attX, attY, attW, attH]);
}
createValues(a) {                // a for array
    let i, j;
    if (isA(a[0])) {             // max 2 dimensions
        let k = 1;
        for (i of a[0]) {
            for (j of a[1]) {
                this[i + j] = k;
                k *= 2;
            }
        }
    }
    else {                       // 1D array is simple loop
        for (i = 0, j = 1; i < a.length; i++, j *= 2)
            this[a[i]] = j;
   }
}

2D-массив предназначен для атрибута SVG feColorMatrix values, который представляет собой матрицу RGBA 4x5 от RGBAM, где M - множитель. Полученные Ez-свойства - это Ez.RR, Ez.RG и т.д.

0

Нелегко написать идеальное перечисление в js, но я нашел Enumify, который является java script lib, и работает достаточно хорошо:

https://github.com/rauschma/enumify

http://2ality.com/2016/01/enumify.html

0

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

var objInvert = function (obj) {
    var invert = {}
    for (var i in obj) {
      if (i.match(/^\d+$/)) i = parseInt(i,10)
      invert[obj[i]] = i
    }
    return invert
}
 
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL',
'BOSSA-NOVA','POP','INDIE']))

console.log(musicStyles)
0

Вы можете попробовать использовать https://bitbucket.org/snippets/frostbane/aAjxM.

my.namespace.ColorEnum = new Enum(
    "RED = 0",
    "GREEN",
    "BLUE"
)

Он должен работать до ie8.

0

Решение Alien должно сделать все как можно проще:

  • использовать ключевое слово enum (зарезервировано в javascript)
  • Если ключевое слово enum просто зарезервировано, но не реализовано в вашем javascript, определите следующее

    const enumerate = spec => spec.split(/\s*,\s*/)
      .reduce((e, n) => Object.assign(e,{[n]:n}), {}) 
    

Теперь вы можете легко использовать его

const kwords = enumerate("begin,end, procedure,if")
console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)

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

0
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });

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

if (incommingEnum === DaysEnum.monday) //incommingEnum is monday

EDIT: Если вы собираетесь сериализовать объект (например, в JSON), вы снова будете ID.

  • 0
    {red:{green:{blue:{}}}}.unwindme() !
  • 0
    Пожалуйста, попробуйте это: if (JSON.parse(JSON.stringify(DaysEnum.monday)) != DaysEnum.monday) console.error('Oops!') . Прочтите мой пост в блоге Enums in Javascript, который посвящен именно этой проблеме, чтобы узнать, почему это происходит.
-2

Вы также можете попытаться определить новую функцию и, следовательно, новое пространство имен и добавить к нему переменные, например.

function Color () {};  
Color.RED = 1;
Color.YELLOW = 2;

Как давно кто-то использует пространство имен, предоставленное функцией Color, все будет хорошо. Если вы знаете Java, это своего рода старые перечисления: мы используем класс или интерфейс только для хранения статических атрибутов. Если функция в javascript является своего рода классом, это почти тот же подход.

Я очень простой способ определить перечисления.

Надеюсь, что это поможет!

Привет.

Виктор.

  • 9
    Этот ответ плохо информирован. Когда люди говорят об использовании функций для имитации пространств имен, они говорят об использовании функций для создания замыканий, которые остаются прикрепленными к объекту, который возвращается этой функцией после завершения исходной функции. Ваш пример не делает такой вещи. Ваш пример просто привязывает свойства к объекту, который также является функцией. По сути, ваш пример функционально совпадает с тем, что опубликовал ОП.
  • 0
    я был прагматичным, а не теоретическим. Я только отвечал на вопрос, и для ТОЛЬКО определения констант нет большой пользы от знания дальше, чем это. Имя всегда нужно помнить, всегда.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню