Я согласен с ответом от zacherates.
Но вы можете сделать вызов intern () в ваших нелиберальных строках.
Из примера zacherates:
// ... but they are not the same object
new String("test") == "test" ==> false
Если вы ставите нелитеральное равенство строки, это правда
new String("test").intern() == "test" ==> true
Начиная с 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 .
Цитаты не нужны, но я сохранил их для согласованности.
В 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 {/*....*/}
Вот что мы все хотим:
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');
С момента написания, октябрь 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.
Еще раз, если вы просто случайный пользователь или в браузере и т. д., просто возьмите заводскую часть кода, потенциально удалив также библиотеку подчеркивания, если вы не хотите использовать ее в своем коде .
Вот несколько разных способов реализации Перечисления типов .
Самый простой способ - просто перебрать объект, добавив инвертированные пары ключ-значение к объекту. Единственным недостатком является то, что вы должны вручную установить значение для каждого члена.
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
Это решение, которое я использую.
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 метода для сопоставления перечислений из объектов сообщения.
Некоторые преимущества этого подхода:
Некоторые недостатки:
Это просто использовать, я думаю. 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"]));
Я изменил решение 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
Я играл с этим, так как мне нравятся мои перечисления. =)
Используя 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
return this;
в конец Enum, вы можете сделать: var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
– HBP
21 August 2013 в 18:15
false
является значением по умолчанию для writable
, enumerable
и configurable
. Не нужно пережевывать дефолты.
– ceving
2 November 2015 в 17:03
Это не очень ответ, но я бы сказал, что это работает отлично, лично
Сказав это, поскольку не имеет значения, каковы значения (вы использовали 0 , 1, 2), я бы использовал значимую строку в случае, если вы когда-либо хотели вывести текущее значение.
Object.freeze()
. Это предотвратит изменение другими значениями кода. Пример: var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
– Sildoreth
16 January 2014 в 21:29
Я только что опубликовал пакет 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;
. Одна очень полезная функция 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: Что это за прокси?
Я помню, когда я впервые начал видеть слово «прокси» всюду, это определенно не имело для меня смысла в течение длительного времени. Если это вы прямо сейчас, я думаю, что простой способ обобщить доверенных лиц - это думать о них как о программном обеспечении, учреждениях или о людях, которые выступают в качестве посредников или посредников между двумя серверами, компаниями или людьми.
Я сделал это некоторое время назад, используя смесь __defineGetter__
и __defineSetter__
или defineProperty
в зависимости от версии JS.
Вот функция генерации перечисления, которую я сделал: https : //gist.github.com/gfarrell/6716853
Вы использовали бы это так:
var Colours = Enum('RED', 'GREEN', 'BLUE');
И он создал бы неизменяемую строку: int словарь (enum).
Создать литерал объекта:
const Modes = {
DRAGGING: 'drag',
SCALING: 'scale',
CLICKED: 'click'
};
const
не делает свойства объекта неизменяемыми, это означает, что переменная Modes
не может быть переназначена на что-то еще. Чтобы сделать его более полным, используйте Object.freeze()
рядом с const
.
– rvighne
12 July 2016 в 22:47
Самое простое решение:
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
«Перфорированный синтаксис» большинства людей уже указан выше. Однако существует серьезная проблема: производительность. Ни один из приведенных выше ответов не очень мал, и все они раздувают ваш размер кода до крайности.
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, как эта форма перечисления.
Я написал 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
является в основном фабрикой.
Полностью документированный справочник, доступный здесь. Надеюсь, это поможет.
Вы можете сделать что-то вроде этого
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
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
}
Несмотря на то, что в 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
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;
Нижняя строка: вы не можете.
Вы можете подделать его, но вы не получите безопасности типов. Обычно это делается путем создания простого словаря строковых значений, сопоставленных целым значениям. Например:
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
Хотя это, вероятно, не подходит для любого действительного использования перечислений , это идет очень долго.
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
Это старый, который я знаю, но способ, которым он был реализован через интерфейс 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» независимо от порядка объявления.
enum MyEnum { Foo, Bar, Foobar }
– parliament
28 July 2015 в 23:42
var ColorEnum = {
red: {},
green: {},
blue: {}
}
Вам не нужно быть уверенным, что вы не назначаете повторяющиеся числа для разных значений перечисления таким образом. Новый объект получает экземпляр и присваивается всем значениям перечисления.
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
Я сделал класс 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>..."
Быстрый и простой способ:
var Colors = function(){
return {
'WHITE':0,
'BLACK':1,
'RED':2,
'GREEN':3
}
}();
console.log(Colors.WHITE) //this prints out "0"
Я придумал этот подход, который моделируется после перечислений в 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
}
})();
freeze
для обратной совместимости? Например, if (Object.freeze) { Object.freeze(values); }
– FBB
23 September 2015 в 11:25
ваши ответы слишком сложны
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
В большинстве современных браузеров существует примитивный тип данных 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
Symbol
.
– rvighne
12 July 2016 в 22:49
Если вы используете 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()
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });
. Вам не нужно указывать идентификатор, вы можете просто использовать пустой объект для сравнения перечислений.if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
– Gabriel Llamas 7 April 2012 в 11:29if (Object.freeze) { Object.freeze(DaysEnum); }
– saluce 24 August 2012 в 16:56({ monday: {},
и т. Д. Означает, что если вы конвертируете этот объект в JSON через stringify, вы получите[{"day": {}}]
, который не будет работать. – jcollum 1 February 2013 в 02:20var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
. Сравнение объектов, как в моем предыдущем комментарии, намного больше, чем сравнение чисел. – Gabriel Llamas 9 April 2014 в 08:58