Какой самый быстрый способ сделать перечисление на JavaScript? [Дубликат]

Я согласен с ответом от zacherates.

Но вы можете сделать вызов intern () в ваших нелиберальных строках.

Из примера zacherates:

// ... but they are not the same object
new String("test") == "test" ==> false 

Если вы ставите нелитеральное равенство строки, это правда

new String("test").intern() == "test" ==> true 
1745
задан Flip 30 October 2017 в 16:20
поделиться

31 ответ

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

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

или

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

и voila! JS enums.

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

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

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

Источник

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

543
ответ дан Andy 15 August 2018 в 20:30
поделиться
  • 1
    Согласно Wikipedia ( ru.wikipedia.org/wiki/JavaScript#Versions ), он применим к Firefox 4, IE 9, Opera 11.60, и я знаю, что он работает в Chrome. – Artur Czajka 15 March 2012 в 13:05
  • 2
    Это правильный ответ сейчас в 2012 году. Проще: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Вам не нужно указывать идентификатор, вы можете просто использовать пустой объект для сравнения перечислений. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday – Gabriel Llamas 7 April 2012 в 11:29
  • 3
    Для обратной совместимости if (Object.freeze) { Object.freeze(DaysEnum); } – saluce 24 August 2012 в 16:56
  • 4
    Я хотел бы указать, что выполнение ({ monday: {}, и т. Д. Означает, что если вы конвертируете этот объект в JSON через stringify, вы получите [{"day": {}}], который не будет работать. – jcollum 1 February 2013 в 02:20
  • 5
    @Supuhstar Мое мнение по этому вопросу сейчас другое. Не используйте замораживание (), это совершенно бесполезно и пустая трата времени делает «глупый». вещи. Если вы хотите вывести перечисление, просто выделите это: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Сравнение объектов, как в моем предыдущем комментарии, намного больше, чем сравнение чисел. – Gabriel Llamas 9 April 2014 в 08:58

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

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

, затем

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

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

 class ColorEnum  extends Enum {/*....*/}
12
ответ дан Abdennour TOUMI 15 August 2018 в 20:30
поделиться
  • 1
    Не могли бы вы объяснить, почему преимущество родительского класса является преимуществом? Я чувствую, что что-то упускаю! – Jon G 1 February 2017 в 10:57
  • 2
    Не делай этого. new ColorEnum() не имеет никакого смысла. – Bergi 9 June 2017 в 01:43
  • 3
    расширение enum звучит сумасшедшим, на самом деле – Codii 6 July 2017 в 13:30
  • 4
    как только язык не поддерживает его, разумно будет придерживаться этого соглашения и использовать его так! согласен! – Marcelo Filho 6 October 2017 в 07:31

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

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

Edit - небольшое улучшение - теперь с varargs: (к сожалению, он не работает должным образом в IE: S ... должен придерживаться предыдущей версии)

function Enum() {
    for (var i in arguments) {
        this[arguments[i]] = i;
    }
}

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');
46
ответ дан Andre 'Fi' 15 August 2018 в 20:30
поделиться

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

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

3
ответ дан arcseldon 15 August 2018 в 20:30
поделиться
  • 1
    Не могли бы вы разместить ответ с помощью только «здесь», как это сделать как случайный пользователь, который просто хочет перечисления, а не фабрики, подчеркивания или что-нибудь интересное »? – GreenAsJade 2 January 2015 в 04:08
  • 2
    Несмотря на то, что это довольно удивительно для разработчиков, это не очень чистое или удобочитаемое. Решение Enum из OP легче и понятнее, поэтому лучше использовать. Тем не менее, довольно странно, что вы придумали это. – David 17 January 2015 в 20:55

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

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

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
4
ответ дан Blake Bowen 15 August 2018 в 20:30
поделиться

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

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 August 2018 в 20:30
поделиться

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

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

2
ответ дан Community 15 August 2018 в 20:30
поделиться

Я изменил решение 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
8
ответ дан David Miró 15 August 2018 в 20:30
поделиться

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

Используя 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
23
ответ дан Duncan 15 August 2018 в 20:30
поделиться
  • 1
    Если вы добавите return this; в конец Enum, вы можете сделать: var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW'); – HBP 21 August 2013 в 18:15
  • 2
    Я не считал это, поскольку это не мой обычный способ делать что-то. Но ты абсолютно прав! Я отредактирую это. – Duncan 21 August 2013 в 18:20
  • 3
    Мне это очень нравится, хотя я не большой поклонник сбрасывания пространства Object (с глобальной функцией ENUM). Преобразовали это в функцию mkenum и добавили необязательные числовые присвоения = & gt; var mixedUp = mkenum ('BLACK', {RED: 0x0F00, BLUE: 0X0F, GREEN: 0x0F0, WHITE: 0x0FFF, ONE: 1}, TWO, THREE, FOUR); // Добавление моего кода в качестве ответа ниже. Благодарю. – Andrew Philips 5 September 2014 в 19:44
  • 4
    Честно говоря, я больше этого не использую. Я использую Google Closure Compiler, и это не работает слишком хорошо (или это просто усложняет ситуацию), если вы используете настройку Advanced. Поэтому я только что вернулся к стандартной записи объектов. – Duncan 8 September 2014 в 18:32
  • 5
    false является значением по умолчанию для writable, enumerable и configurable. Не нужно пережевывать дефолты. – ceving 2 November 2015 в 17:03

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

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

584
ответ дан Gareth 15 August 2018 в 20:30
поделиться
  • 1
    Вот простая фабрика enum, которую я создал; npmjs.org/package/simple-enum – Tolga E 16 September 2013 в 19:18
  • 2
    Извините, но реализация перечисления имеет несколько проблем. Я рекомендую использовать это вместо: github.com/rauschma/enums – paldepind 26 September 2013 в 19:21
  • 3
    Это было сказано в другом ответе, но поскольку этот ответ является принятым ответом, я отправлю его здесь. Решение OP правильное. Это будет даже лучше, если использовать его с Object.freeze(). Это предотвратит изменение другими значениями кода. Пример: var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2}); – Sildoreth 16 January 2014 в 21:29
  • 4
    @TolgaE спасибо за эту библиотеку! Это вдохновило меня не только довести до минимума, но и добавить пару функций! Я расколол ваш и поставил все здесь: github.com/BlueHuskyStudios/Micro-JS-Enum – Supuhstar 9 April 2014 в 06:26
  • 5
    @Supuhstar Это здорово! Я рад, что вы можете использовать его. Не стесняйтесь делать запрос на вытягивание, если вы хотите, чтобы он был объединен в этой библиотеке, тогда я могу обновить библиотеку npm – Tolga E 9 April 2014 в 16:00

Я только что опубликовал пакет NPM gen_enum позволяет быстро создать структуру данных Enum в Javascript:

var genEnum = require('gen_enum');

var AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD');
var curMode = AppMode.LOG_IN;
console.log(curMode.isLogIn()); // output true 
console.log(curMode.isSignUp()); // output false 
console.log(curMode.isForgotPassword()); // output false 

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

Для получения дополнительной информации, пожалуйста, проверьте https://github.com/greenlaw110/enumjs

Обновления

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

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

var genEnum = require('gen_enum');

-

var genEnum = require('constjs').enum;
4
ответ дан Gelin Luo 15 August 2018 в 20:30
поделиться

Использовать Javascript Proxies

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

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

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

Не поймите меня неправильно, поведение JavaScript возврата undefined при доступе к неопределенным свойства на самом деле очень мощная функция языка, но это не функция, которую вы хотите, когда пытаетесь издеваться над традиционными структурами Enum.

Здесь отображаются объекты прокси. Прокси были стандартизированы на языке с введением ES6 (ES2015). Вот описание из MDN:

Объект Proxy используется для определения пользовательского поведения для основных операций (например, поиск свойств, назначение, перечисление, вызов функции и т. Д.).

Подобно прокси-серверу веб-сервера, прокси-серверы JavaScript могут перехватывать операции над объектами (используя «ловушки», называть их перехватами, если хотите) и позволяют выполнять различные проверки, действия и / или манипуляции до они завершают (или в некоторых случаях вообще останавливают операции, что именно мы хотим сделать, если и когда мы пытаемся ссылаться на перечислитель, которого не существует).

Вот надуманный пример, который использует объект Proxy для имитации Enums. В этом примере перечислениями являются стандартные HTTP-методы (например, «GET», «POST» и т. Д.):

// Generic Factory Function for creating enums (10 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

function createEnum(enumObj) {

  // 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. This can be done by defining the "get" trap
  
  const enumHandler = {
    get: function(target, name) {
      if (target[name]) {
        return target[name]
      }
      throw new Error(`No such enumerator: ${name}`)
    }
  }
  
  
  // Freeze the target object to prevent modifications
  return new Proxy(Object.freeze(enumObj), enumHandler)
}


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

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

httpMethods.delete = "delete" 
// no effect due to Object.freeze, fails silently (no error thrown)

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


ASIDE: Что это за прокси?

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

5
ответ дан Govind Rai 15 August 2018 в 20:30
поделиться

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

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

Вы использовали бы это так:

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

И он создал бы неизменяемую строку: int словарь (enum).

2
ответ дан GTF 15 August 2018 в 20:30
поделиться
  • 1
    Хорошо, я переписал его, и теперь он является агностиком библиотеки (хотя он предполагает какой-то загрузчик AMD, но его можно удалить). – GTF 26 September 2013 в 18:03

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

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
11
ответ дан hvdd 15 August 2018 в 20:30
поделиться
  • 1
    const не делает свойства объекта неизменяемыми, это означает, что переменная Modes не может быть переназначена на что-то еще. Чтобы сделать его более полным, используйте Object.freeze() рядом с const. – rvighne 12 July 2016 в 22:47
  • 2
    – Jack Giffin 7 October 2018 в 13:21

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

Создать

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
ответ дан Ilya Gazman 15 August 2018 в 20:30
поделиться

«Перфорированный синтаксис» большинства людей уже указан выше. Однако существует серьезная проблема: производительность. Ни один из приведенных выше ответов не очень мал, и все они раздувают ваш размер кода до крайности.

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

// later on

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

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

(Длина: 2,450 байт)

(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 байта (короче!)) Minified Without Enumerations (длина: 843 байта (дольше) )

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

5
ответ дан Jack Giffin 15 August 2018 в 20:30
поделиться

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

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 является в основном фабрикой.

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

2
ответ дан Jules Randolph 15 August 2018 в 20:30
поделиться
  • 1
    На самом деле это не перечисление. Но я вижу, что это очень полезно. – Will 14 July 2016 в 17:23
  • 2
    @Will, как бы вы их назвали ^^? AugmentedEnum? – Jules Randolph 14 July 2016 в 18:07
  • 3
    Я не уверен ... Это своего рода смесь перечислений + метаданных (в вашем примере). Перечисление просто имеет имя и значение структуры, которое может быть отброшено в / из. Вы можете поместить неограниченное количество свойств для каждого, используя это. Я видел шаблон раньше, и команда, которая его писала, называлась константами результата ... Включая отдельные константные типы, такие как DataType, которые не только имели имя, но имели значение ID, действительные операторы (другая группа констант! ), действительные преобразования и т. д. Действительно полезно, но гораздо больше, чем просто простое перечисление. – Will 14 July 2016 в 18:13
  • 4
    Очень плохо работает. Делает весь код медленнее. Делает уменьшенный код в несколько раз больше. Очень плохо. – Jack Giffin 23 June 2018 в 23:27

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

function Enum(){
  this.add.apply(this,arguments);
}

Enum.prototype.add = function(){
  for (var i in arguments) {
    this[arguments[i]] = new String(arguments[i]);
  }
};
Enum.prototype.toList = function(){
  return Object.keys(this)
};

var STATUS = new Enum("CLOSED","PENDING");


var STATE = new Enum("CLOSED","PENDING");

STATE.CLOSED === STATUS.CLOSED  // false;
STATE.CLOSED === "CLOSED"  // false;
STATE.CLOSED.toString() === "CLOSED"  // true;

Как определено в этой библиотеке. https://github.com/webmodule/foo/blob/master/foo.js#L217

3
ответ дан LNT 15 August 2018 в 20:30
поделиться

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

В моем текущем игровом проекте, который я использовал ниже, поскольку некоторые клиенты все еще используют 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'
};

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

Назначение:

var wildType = CONST_WILD_TYPES.REGULAR;

Сравнение:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}
5
ответ дан Manohar Reddy Poreddy 15 August 2018 в 20:30
поделиться

Несмотря на то, что в 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 «перечислению», а не к другому перечислению (что было бы невозможно отличить, если вы использовали общие объекты или символы).

update

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

  1. не может быть создано больше объектов типа CellState
  2. , вам гарантировано, что не назначены два экземпляра перечисления тот же самый код
  3. метод утилиты, чтобы получить перечисление обратно из строкового представления
  4. , функция values, которая возвращает все экземпляры перечисления, не должна создавать возвращаемое значение в выше, ручной (и подверженный ошибкам) ​​способ.
    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;
    
4
ответ дан Marcus Junius Brutus 15 August 2018 в 20:30
поделиться

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

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

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

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

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

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

Изменить

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

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

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

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

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

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

79
ответ дан Randolpho 15 August 2018 в 20:30
поделиться
  • 1
    есть безопасность типа в javascript? – Scott Evernden 21 August 2009 в 22:02
  • 2
    Поэтому не сопоставляйте значения свойствам объекта. Используйте getter для доступа к перечисляемому (хранящемуся как свойство, скажем, «частного» объекта). Наивная реализация будет выглядеть как var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1 – kangax 22 August 2009 в 04:20
  • 3
    @Scott Evernden: точка взята. @kangax: дело в том, что это все еще хак. Перечисления просто не существуют в Javascript, периоде, конце истории. Даже образец, предложенный Тимом Сильвестром, по-прежнему менее идеален. – Randolpho 22 August 2009 в 21:14
  • 4
    Посыпать код литералами не очень удобно, поэтому имеет смысл создавать для него константы. Конечно, Javascript также не имеет констант. Таким образом, в основном это всего лишь способ написать чистый код. Он не может быть применен, но не много в Javascript. Вы можете переопределить константы, или функции, или в основном что угодно. EG: document.getElementById = function () {alert ("Вы ввернуты. Javascript не является типичным. & Quot;);}; – Stijn de Witt 29 November 2011 в 13:04
  • 5
    @Randolpho: А как насчет объекта Artur Czajka Object.freeze? Разве это не помешало бы вам установить понедельник в четверг? – Michael 5 January 2012 в 17:58

Это старый, который я знаю, но способ, которым он был реализован через интерфейс 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 15 August 2018 в 20:30
поделиться
  • 1
    Plus MyEnum [& quot; Bar & quot;] работает, который возвращает 1 ... & lt; 3 TypeScript до сих пор ... – David Karlaš 5 January 2014 в 11:41
  • 2
    и, конечно, если вы на самом деле используете «Машинопись»: enum MyEnum { Foo, Bar, Foobar } – parliament 28 July 2015 в 23:42
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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

3
ответ дан Shivanshu Goyal 15 August 2018 в 20:30
поделиться
  • 1
    Этот ответ недооценен. Это одна из моих любимых идей для ее простоты. На практике, я думаю, что я буду придерживаться строк, потому что на этот раз легче отлаживать. – Jacque Goupil 8 December 2015 в 22:16

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


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

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

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

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

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

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

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

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

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

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

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

EG:

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

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

477
ответ дан Stijn de Witt 15 August 2018 в 20:30
поделиться
  • 1
    так как же вы идете и создаете просто РАЗМЕР, если у вас есть только его имя? – Johanisma 10 November 2011 в 06:06
  • 2
    @Johanisma: этот случай использования не имеет смысла для перечислений, поскольку вся их идея заключается в том, что вы заранее знаете все ценности. Однако ничто не мешает вам добавлять дополнительные значения позже в Javascript. Я добавлю пример этого к моему ответу. – Stijn de Witt 29 November 2011 в 12:43
  • 3
    Очень хорошо ответил. Я являюсь случайным пользователем JS / JQ, когда это необходимо, и это помогло в ситуации с SharePoint, которая нуждалась в решении. – GoldBishop 20 March 2014 в 14:46
  • 4
    +1 для ссылки на ваш пост с помощью подхода свойств. Элегантный в том, что основные декларации просты, как и в OP, с добавленными свойствами, если хотите. – goodeye 29 April 2014 в 16:35
  • 5
    @DavideAndrea Вы правы. С тех пор я начал размещать свой блог под своим собственным доменным именем. Обновлена ​​ссылка. Спасибо, что упомянули об этом! – Stijn de Witt 9 September 2017 в 18:42

Я сделал класс 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 C #):

var val2 = enum1.item2;

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

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

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

var arr = enum1.GetObjArr();

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

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

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

var html = enum1.GetSelectOptionsHTML();

:

"<option value='0'>item1</option>..."
5
ответ дан Tim Kara 15 August 2018 в 20:30
поделиться

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

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

console.log(Colors.WHITE)  //this prints out "0"
4
ответ дан user2254487 15 August 2018 в 20:30
поделиться
  • 1
    Функция не нужна и дает тот же результат, что и OP. – Sildoreth 16 January 2014 в 21:19

Я придумал этот подход, который моделируется после перечислений в Java.

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

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
    }

})();
4
ответ дан Vivin Paliath 15 August 2018 в 20:30
поделиться
  • 1
    Это выглядит хорошо, может быть, вам стоит проверить существование метода freeze для обратной совместимости? Например, if (Object.freeze) { Object.freeze(values); } – FBB 23 September 2015 в 11:25
  • 2
    Хорошая точка зрения! Сделаю! – Vivin Paliath 23 September 2015 в 14:06

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

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
9
ответ дан Xeltor 15 August 2018 в 20:30
поделиться

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

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

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

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

Демонстрация плунжера

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

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

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE
41
ответ дан YakovL 15 August 2018 в 20:30
поделиться
  • 1
    Это правильный ответ в теории. На практике поддержка браузера в 2015 году далека от достаточной. Не готово к производству. – vbraun 16 June 2015 в 08:14
  • 2
    Хотя поддержки браузера еще нет, это лучший ответ, так как это близко к тому, для чего предназначен Symbol. – rvighne 12 July 2016 в 22:49
  • 3
    Значения meh ... enum часто должны быть сериализуемыми, но символы не очень удобны для сериализации и десериализации. – Andy 25 July 2018 в 00:09

Если вы используете 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()
12
ответ дан Yaroslav 15 August 2018 в 20:30
поделиться