tipos - ¿Cuál es la sintaxis preferida para definir enumeraciones en JavaScript?
tipos de funciones en javascript (30)
¿Cuál es la sintaxis preferida para definir enumeraciones en JavaScript? Algo como:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
¿O hay un idioma más preferible?
Usa Javascript Proxies
TLDR: agregue esta clase a sus métodos de utilidad y utilícela en todo su código, simula el comportamiento de Enum desde los lenguajes de programación tradicionales y, en realidad, produce errores cuando intenta acceder a un enumerador que no existe o agregar / actualizar un enumerador. No hay necesidad de confiar en Object.freeze()
.
class Enum {
constructor(enumObj) {
const handler = {
get(target, name) {
if (typeof target[name] != ''undefined'') {
return target[name];
}
throw new Error(`No such enumerator: ${name}`);
},
set() {
throw new Error(''Cannot add/update properties on an Enum instance after it is defined'')
}
};
return new Proxy(enumObj, handler);
}
}
Luego crea enumeraciones al crear una instancia de la clase:
const roles = new Enum({
ADMIN: ''Admin'',
USER: ''User'',
});
Explicación completa:
Una característica muy beneficiosa de los Enums que obtienes de los lenguajes tradicionales es que explotan (lanzan un error en tiempo de compilación) si intentas acceder a un enumerador que no existe.
Además de congelar la estructura de enumeración simulada para evitar que se agreguen accidentalmente / maliciosamente valores adicionales, ninguna de las otras respuestas aborda esa característica intrínseca de Enums.
Como probablemente sepa, el acceso a miembros no existentes en JavaScript simplemente devuelve undefined
y no revienta su código. Como los enumeradores son constantes predefinidas (es decir, los días de la semana), nunca debe haber un caso en el que un enumerador no esté definido.
No me malinterpretes, el comportamiento de JavaScript de volver undefined
al acceder a propiedades no definidas es en realidad una característica muy poderosa del lenguaje, pero no es una característica que quieras cuando intentas burlarte de las estructuras de Enum tradicionales.
Aquí es donde brillan los objetos Proxy. Los proxies se estandarizaron en el idioma con la introducción de ES6 (ES2015). Aquí está la descripción de MDN:
El objeto Proxy se utiliza para definir un comportamiento personalizado para operaciones fundamentales (por ejemplo, búsqueda de propiedades, asignación, enumeración, invocación de funciones, etc.).
De manera similar a un proxy de servidor web, los proxies de JavaScript pueden interceptar operaciones en objetos (con el uso de "trampas", llamarlos enganches si lo desea) y permitirle realizar varias verificaciones, acciones y / o manipulaciones antes de que se completen (o en algunos casos, detener las operaciones por completo, lo que es exactamente lo que queremos hacer cuando intentamos hacer referencia a un enumerador que no existe).
Aquí hay un ejemplo ideado que usa el objeto Proxy para imitar Enums. Los enumeradores en este ejemplo son métodos HTTP estándar (es decir, "GET", "POST", etc.):
// Class for creating enums (13 lines)
// Feel free to add this to your utility library in
// your codebase and profit! Note: As Proxies are an ES6
// feature, some browsers/clients may not support it and
// you may need to transpile using a service like babel
class Enum {
// The Enum class instantiates a JavaScript Proxy object.
// Instantiating a `Proxy` object requires two parameters,
// a `target` object and a `handler`. We first define the handler,
// then use the handler to instantiate a Proxy.
// A proxy handler is simply an object whose properties
// are functions which define the behavior of the proxy
// when an operation is performed on it.
// For enums, we need to define behavior that lets us check what enumerator
// is being accessed and what enumerator is being set. This can be done by
// defining "get" and "set" traps.
constructor(enumObj) {
const handler = {
get(target, name) {
if (typeof target[name] != ''undefined'') {
return target[name]
}
throw new Error(`No such enumerator: ${name}`)
},
set() {
throw new Error(''Cannot add/update properties on an Enum instance after it is defined'')
}
}
// Freeze the target object to prevent modifications
return new Proxy(enumObj, handler)
}
}
// Now that we have a generic way of creating Enums, lets create our first Enum!
const httpMethods = new Enum({
DELETE: "DELETE",
GET: "GET",
OPTIONS: "OPTIONS",
PATCH: "PATCH",
POST: "POST",
PUT: "PUT"
})
// Sanity checks
console.log(httpMethods.DELETE)
// logs "DELETE"
try {
httpMethods.delete = "delete"
} catch (e) {
console.log("Error: ", e.message)
}
// throws "Cannot add/update properties on an Enum instance after it is defined"
try {
console.log(httpMethods.delete)
} catch (e) {
console.log("Error: ", e.message)
}
// throws "No such enumerator: delete"
ASIDE: ¿Qué diablos es un proxy?
Recuerdo que cuando empecé a ver la palabra proxy en todas partes, definitivamente no tenía sentido para mí durante mucho tiempo. Si ese es usted en este momento, creo que una forma fácil de generalizar los proxies es pensar en ellos como software, instituciones o incluso personas que actúan como intermediarios o intermediarios entre dos servidores, empresas o personas.
Desde 1.8.5 es posible sellar y congelar el objeto, entonces defina lo anterior como:
var DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})
o
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)
¡y voilá! JS enums.
Nota: Lo escribí en 2011, pero es 2019: use const
para evitar que se sobrescriba su diccionario de enumeración.
Sin embargo, esto no le impide asignar un valor no deseado a una variable, que a menudo es el objetivo principal de las enumeraciones:
let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors
Una forma de garantizar un mayor grado de seguridad de tipos (con enumeraciones o de otro tipo) es usar una herramienta como TypeScript o Flow .
Las citas no son necesarias, pero las mantuve por coherencia.
En la mayoría de los navegadores modernos, hay un tipo de datos primitivo de symbol que se puede utilizar para crear una enumeración. Asegurará el tipo de seguridad de la enumeración ya que cada valor de símbolo está garantizado por JavaScript para ser único, es decir, Symbol() != Symbol()
. Por ejemplo:
const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});
Para simplificar la depuración, puede agregar una descripción a los valores de enumeración:
const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});
En GitHub puede encontrar un contenedor que simplifica el código requerido para inicializar la enumeración:
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
En pocas palabras: no se puede.
Puedes fingirlo, pero no obtendrás el tipo de seguridad. Normalmente, esto se hace creando un diccionario simple de valores de cadena asignados a valores enteros. Por ejemplo:
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Document.Write("Enumerant: " + DaysEnum.tuesday);
¿El problema con este enfoque? Puede redefinir accidentalmente su enumerante, o accidentalmente tener valores duplicados duplicados. Por ejemplo:
DaysEnum.monday = 4; // whoops, monday is now thursday, too
Editar
¿Qué pasa con el Object.freeze de Artur Czajka? ¿No funcionaría eso para evitar que programes de lunes a jueves? - Fry Quad
Absolutamente, Object.freeze
arreglaría totalmente el problema del que me quejé. Me gustaría recordarles a todos que cuando escribí lo anterior, Object.freeze
no existía realmente.
Ahora .... ahora abre algunas posibilidades muy interesantes.
Editar 2
Aquí hay una muy buena biblioteca para crear enumeraciones.
http://www.2ality.com/2011/10/enums.html
Si bien es probable que no se adapte a todos los usos válidos de enumeraciones, va un largo camino.
Esta es la solución que uso.
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;
}
Y definas tus enums de esta manera:
var COLORS = defineEnum({
RED : {
value : 1,
string : ''red''
},
GREEN : {
value : 2,
string : ''green''
},
BLUE : {
value : 3,
string : ''blue''
}
});
Y así es como accedes a tus enumeraciones:
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
});
Usualmente uso los últimos 2 métodos para mapear enumeraciones de objetos de mensaje.
Algunas ventajas de este enfoque:
- Fácil de declarar enumeraciones
- Fácil acceso a sus enums
- Sus enums pueden ser tipos complejos
- La clase Enum tiene cierto almacenamiento en caché asociativo si está utilizando mucho getByValue
Algunas desventajas:
- Hay un poco de gestión de memoria desordenada allí, ya que mantengo las referencias a las enumeraciones
- Todavía no hay seguridad tipo
Este es uno antiguo que conozco, pero la forma en que se implementó a través de la interfaz de TypeScript es:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["Foo"] = 0] = "Foo";
MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));
Esto le permite buscar tanto en MyEnum.Bar
que devuelve 1 como en MyEnum[1]
que devuelve "Barra" independientemente del orden de la declaración.
Esto es lo que todos queremos:
function Enum(constantsList) {
for (var i in constantsList) {
this[constantsList[i]] = i;
}
}
Ahora puedes crear tus enums:
var YesNo = new Enum([''NO'', ''YES'']);
var Color = new Enum([''RED'', ''GREEN'', ''BLUE'']);
Al hacer esto, se puede acceder a las constantes de la forma habitual (SíNo.SÍ, Color.GREEN) y obtienen un valor int secuencial (NO = 0, SÍ = 1; ROJO = 0, VERDE = 1, AZUL = 2).
También puede agregar métodos, utilizando 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 */
};
Edición - pequeña mejora - ahora con varargs: (desafortunadamente no funciona correctamente en IE: S ... debería seguir con la versión anterior)
function Enum() {
for (var i in arguments) {
this[arguments[i]] = i;
}
}
var YesNo = new Enum(''NO'', ''YES'');
var Color = new Enum(''RED'', ''GREEN'', ''BLUE'');
Esto no es una gran respuesta, pero diría que funciona bien, personalmente
Dicho esto, dado que no importa cuáles sean los valores (has usado 0, 1, 2), usaría una cadena significativa en caso de que alguna vez quisieras generar el valor actual.
He estado jugando con esto, ya que amo a mis enumerados. =)
Usando Object.defineProperty
, creo que se me ocurrió una solución bastante viable.
Aquí hay un jsfiddle: http://jsfiddle.net/ZV4A6/
Usando este método ... debería (en teoría) poder llamar y definir valores de enumeración para cualquier objeto, sin afectar otros atributos de ese objeto.
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
});
Debido al atributo de writable:false
esto debería hacer que sea seguro.
Así que deberías poder crear un objeto personalizado, luego llamar a Enum()
en él. Los valores asignados comienzan en 0 e incrementan por artículo.
var EnumColors={};
EnumColors.Enum(''RED'',''BLUE'',''GREEN'',''YELLOW'');
EnumColors.RED; // == 0
EnumColors.BLUE; // == 1
EnumColors.GREEN; // == 2
EnumColors.YELLOW; // == 3
La "Sintaxis preferida" por la mayoría de las personas ya se ha enumerado anteriormente. Sin embargo, hay un problema principal: el tamaño del código. Cada otra respuesta que aparece aquí aumenta el tamaño de su código hasta el extremo. Para obtener el mejor rendimiento posible, la legibilidad del código, la gestión de proyectos a gran escala, las sugerencias de sintaxis en muchos editores de códigos y la reducción del tamaño del código por reducción, esta es la forma correcta de hacer las enumeraciones.
const ENUM_COLORENUM_RED = 0,
ENUM_COLORENUM_GREEN = 1,
ENUM_COLORENUM_BLUE = 2,
ENUMLEN_COLORENUM = 3;
// later on
if(currentColor === ENUM_COLORENUM_RED) {
// whatever
}
Además, esta sintaxis permite una clase clara y concisa que se extiende como se ve a continuación.
(Longitud: 2,450 bytes)
(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(inputString) {
var rawstr = inputString.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);
Algunos pueden decir que esto es menos práctico que otras soluciones: elimina toneladas de espacio, lleva mucho tiempo escribir y no está recubierto con sintaxis de azúcar. Esas personas tendrían razón si no minimizan su código. Sin embargo, ninguna persona razonable dejaría un código no minado en el producto final. Para esta minificación, Closure Compiler es lo mejor que todavía tengo que encontrar. El acceso en línea se puede encontrar here . El compilador de cierre es capaz de tomar todos estos datos de enumeración e integrarlos, haciendo que tu Javascript sea super duper pequeño y se ejecute super duper rápidamente. Observar.
(Longitud: 605 bytes)
''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);
Ahora, veamos qué tan grande sería el archivo equivalente sin ninguna de estas enumeraciones.
Fuente sin utilizar enumeraciones (longitud: 1,973 bytes (477 bytes más cortos que el código enumerado))
Reducido sin usar enumeraciones (longitud: 843 bytes (238 bytes más que el código enumerado ))
Como se ve, sin enumeración, el código fuente es más corto al costo de un código minificado más grande. No sé sobre ti; pero estoy seguro de que no incorporo el código fuente en el producto final. Por lo tanto, esta forma de enumeración es muy superior en la medida en que da como resultado tamaños de archivo minificados más pequeños.
Otra ventaja de esta forma de enumeración es que se puede usar para administrar fácilmente proyectos a gran escala sin sacrificar el tamaño del código reducido. Cuando se trabaja en un proyecto grande con muchas otras personas, podría ser beneficioso marcar y etiquetar explícitamente los nombres de las variables con quienes crearon el código para que el creador original del código pueda ser identificado rápidamente para corregir errores en colaboración.
// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED = 0,
ENUM_JG_COLORENUM_GREEN = 1,
ENUM_JG_COLORENUM_BLUE = 2,
ENUMLEN_JG_COLORENUM = 3;
// later on
if(currentColor === ENUM_JG_COLORENUM_RED) {
// whatever
}
// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED = 0,
ENUM_PL_ARRAYTYPE_ISSORTED = 1,
ENUM_BK_ARRAYTYPE_CHUNKED = 2, // added by Bob Knight
ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
ENUMLEN_PL_COLORENUM = 4;
// later on
if(
randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
// whatever
}
Además, esta forma de enumeración también es mucho más rápida después de la reducción. En las propiedades con nombre normales, el navegador tiene que usar mapas de hash para buscar dónde está la propiedad en el objeto. Aunque los compiladores JIST almacenan de manera inteligente esta ubicación en el objeto, todavía hay una sobrecarga tremenda para casos especiales, como eliminar una propiedad inferior del objeto. Pero, con matrices PACKED_ELEMENTS índice entero no dispersas continuas, el navegador puede omitir gran parte de esa sobrecarga porque el índice del valor en la matriz interna ya está especificado. Sí, de acuerdo con el estándar ECMAScript, todas las propiedades deben tratarse como cadenas. Sin embargo, este aspecto del estándar ECMAScript es muy engañoso sobre el rendimiento porque todos los navegadores tienen optimizaciones especiales para índices numéricos en matrices.
Además, mi cereza personal en la parte superior está utilizando esta forma de enumeración junto con el editor de texto CodeMirror en modo Javascript. El modo de resaltado de sintaxis de Javascript de CodeMirror resalta las variables locales en el alcance actual. De esa manera, sabrá al instante cuando escribe un nombre de variable correctamente porque si el nombre de la variable se declaró previamente con la palabra clave var
, entonces el nombre de la variable cambia a un color especial (cian por defecto). Incluso si no usa CodeMirror, al menos el navegador arroja una útil [variable name] is not defined
excepción [variable name] is not defined
al ejecutar código con nombres de enumeración mal escritos. Además, las herramientas de JavaScript como JSLint y Closure Compiler son muy fuertes al decirle cuando escribe mal un nombre de variable de enumeración. CodeMirror, el navegador y varias herramientas de Javascript juntas hacen que la depuración de esta forma de enumeración sea muy simple y realmente fácil.
Por lo tanto, llego a la conclusión de que, de hecho, esta forma de enumeración es la forma de avanzar no solo para el tamaño del código reducido, sino también para el rendimiento, la claridad y la colaboración.
ACTUALIZACIÓN : Gracias por todos los upvotes a todos, pero no creo que mi respuesta a continuación sea la mejor manera de escribir enumeraciones en Javascript. Ver mi blog para más detalles: Enums en Javascript .
Alertar el nombre ya es posible:
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);
}
}
Alternativamente, puedes hacer que los valores sean objetos, para que puedas tener la torta y comerla también:
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);
}
En Javascript, como es un lenguaje dinámico, incluso es posible agregar valores de enumeración al conjunto más adelante:
// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};
Recuerde, los campos de la enumeración (valor, nombre y código en este ejemplo) no son necesarios para la verificación de identidad y solo están allí para su comodidad. Además, el nombre de la propiedad de tamaño en sí no necesita estar codificado, sino que también puede establecerse dinámicamente. Entonces, suponiendo que solo conoce el nombre de su nuevo valor de enumeración, aún puede agregarlo sin problemas:
// Add ''Extra Large'' size, only knowing it''s name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};
Por supuesto, esto significa que ya no se pueden hacer algunas suposiciones (ese valor representa el orden correcto para el tamaño, por ejemplo).
Recuerde, en Javascript un objeto es como un mapa o una tabla hash. Un conjunto de pares nombre-valor. Puede recorrerlos o manipularlos sin saber mucho de antemano.
P.EJ:
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.
}
}
Y por cierto, si está interesado en los espacios de nombres, es posible que desee echar un vistazo a mi solución para el simple pero potente espacio de nombres y la gestión de dependencias para javascript: Paquetes JS
La solución más simple:
Crear
var Status = Object.freeze({
"Connecting":0,
"Ready":1,
"Loading":2,
"Processing": 3
});
Obtener valor
console.log(Status.Ready) // 1
Obtener la clave
console.log(Object.keys(Status)[Status.Ready]) // Ready
Crear un objeto literal:
const Modes = {
DRAGGING: ''drag'',
SCALING: ''scale'',
CLICKED: ''click''
};
He modificado la solución de 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);
}
}
Prueba:
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
Puedes hacer algo como esto
var Enum = (function(foo) {
var EnumItem = function(item){
if(typeof item == "string"){
this.name = item;
} else {
this.name = item.name;
}
}
EnumItem.prototype = new String("DEFAULT");
EnumItem.prototype.toString = function(){
return this.name;
}
EnumItem.prototype.equals = function(item){
if(typeof item == "string"){
return this.name == item;
} else {
return this == item && this.name == item.name;
}
}
function Enum() {
this.add.apply(this, arguments);
Object.freeze(this);
}
Enum.prototype.add = function() {
for (var i in arguments) {
var enumItem = new EnumItem(arguments[i]);
this[enumItem.name] = enumItem;
}
};
Enum.prototype.toList = function() {
return Object.keys(this);
};
foo.Enum = Enum;
return Enum;
})(this);
var STATUS = new Enum("CLOSED","PENDING", { name : "CONFIRMED", ackd : true });
var STATE = new Enum("CLOSED","PENDING","CONFIRMED",{ name : "STARTED"},{ name : "PROCESSING"});
Como se define en esta biblioteca. https://github.com/webmodule/foo/blob/master/foo.js#L217
Ejemplo completo https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026
Una forma rápida y sencilla sería:
var Colors = function(){
return {
''WHITE'':0,
''BLACK'':1,
''RED'':2,
''GREEN'':3
}
}();
console.log(Colors.WHITE) //this prints out "0"
tus respuestas son demasiado complicadas
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
A partir de octubre de 2014 , así que aquí hay una solución contemporánea. Estoy escribiendo la solución como un módulo de nodo, y he incluido una prueba con Mocha y Chai, además de subrayar JS. Puede ignorarlos fácilmente, y simplemente tome el código Enum si lo prefiere.
He visto muchas publicaciones con bibliotecas demasiado complicadas, etc. La solución para obtener soporte de enumeración en Javascript es tan simple que realmente no es necesaria. Aquí está el código:
Archivo: 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;
Y una prueba para ilustrar lo que te da:
archivo: 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'');
});
});
});
Como puede ver, obtiene una fábrica de Enum, puede obtener todas las claves simplemente llamando a enum.keys, y puede hacer coincidir las claves con las constantes de enteros. Y puede reutilizar la fábrica con diferentes valores y exportar las Enums generadas utilizando el enfoque modular de Node.
Una vez más, si usted es solo un usuario casual, o en el navegador, etc., simplemente tome la parte de fábrica del código, eliminando potencialmente la biblioteca de guión bajo también si no desea usarla en su código.
Acabo de publicar un paquete NPM gen_enum te permite crear la estructura de datos Enum en Javascript rápidamente:
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
Una cosa buena de esta pequeña herramienta es en el entorno moderno (incluidos los navegadores nodejs e IE 9+), el objeto Enum devuelto es inmutable.
Para obtener más información, visite https://github.com/greenlaw110/enumjs
Actualizaciones
constjs obsoleto el gen_enum
paquete y constjs la función en el paquete constjs , que proporciona más funciones que incluyen objetos inmutables, deserialización de cadenas JSON, constantes de cadenas y generación de mapas de bits, etc. Visite constjs para obtener más información
Para actualizar desde gen_enum
que constjs
acaba de cambiar la declaración
var genEnum = require(''gen_enum'');
a
var genEnum = require(''constjs'').enum;
Aquí hay un par de maneras diferentes para implementar las enumeraciones de TypeScript .
La forma más sencilla es simplemente iterar sobre un objeto, agregando pares de clave-valor invertidos al objeto. El único inconveniente es que debe establecer manualmente el valor para cada miembro.
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
Y aquí hay un mixin de lodash para crear una enumeración usando una cadena. Si bien esta versión es un poco más complicada, realiza la numeración automáticamente para usted. Todos los métodos de lodash utilizados en este ejemplo tienen un equivalente de JavaScript regular, por lo que puede cambiarlos fácilmente si lo desea.
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
Así es como Typescript lo traduce enum
a Javascript:
var makeEnum = function(obj) {
obj[ obj[''Active''] = 1 ] = ''Active'';
obj[ obj[''Closed''] = 2 ] = ''Closed'';
obj[ obj[''Deleted''] = 3 ] = ''Deleted'';
}
Ahora:
makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}
Al principio estaba confundido por qué obj[1]
devuelve ''Active''
, pero luego me di cuenta de que es muy simple: el operador de asignación asigna un valor y luego lo devuelve:
obj[''foo''] = 1
// => 1
Aunque solo los métodos estáticos (y no las propiedades estáticas) son compatibles con ES2015 (ver here también, §15.2.2.2), curiosamente, puede usar los siguientes con Babel con el es2015
preajuste:
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);
Descubrí que esto funciona como se esperaba, incluso a través de los módulos (por ejemplo, la importación de la CellState
enumeración de otro módulo) y también cuando importo un módulo usando Webpack.
La ventaja que este método tiene sobre la mayoría de las otras respuestas es que puede usarlo junto con un verificador de tipo estático (por ejemplo, Flow ) y puede afirmar, en el momento del desarrollo utilizando la verificación de tipo estática, que sus variables, parámetros, etc. son específicos CellState
" enumeración "en lugar de alguna otra enumeración (que sería imposible distinguir si usara símbolos o objetos genéricos).
actualizar
El código anterior tiene una deficiencia en el sentido de que permite crear objetos adicionales de tipo CellState
(aunque no se pueden asignar a los campos estáticos CellState
desde que se congela). Aún así, el código más refinado a continuación ofrece las siguientes ventajas:
- no
CellState
se pueden crear más objetos de tipo - se le garantiza que no hay dos instancias de enumeración asignadas al mismo código
- Método de utilidad para recuperar la enumeración de una representación de cadena
La
values
función que devuelve todas las instancias de la enumeración no tiene que crear el valor de retorno de la manera anterior, manual (y propensa a errores)''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;
En ES7 , puede hacer un ENUM elegante basándose en atributos estáticos:
class ColorEnum {
static RED = 0 ;
static GREEN = 1;
static BLUE = 2;
}
entonces
if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}
La ventaja (de la utilización de clase en lugar de objeto literal) es tener una clase padre Enum
entonces todas sus enumeraciones se extiende esa clase.
class ColorEnum extends Enum {/*....*/}
Es fácil de usar, creo. https://.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);
ACTUALIZAR:
Hay mis códigos de ayuda ( 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"]));
Escribí enumeración en una biblioteca muy pequeña para abordar el problema que garantiza la seguridad de los tipos , permitir que las constantes de enumeración hereden de un prototipo , las constantes de enumeración de las garantías y los tipos de enumeración sean inmutables + muchas características pequeñas. Permite refactorizar una gran cantidad de código y mover algo de lógica dentro de la definición de enumeración. Aquí hay un ejemplo :
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
Es básicamente una fábrica.
Guía completamente documentada disponible aquí. Espero que esto ayude.
He creado una clase Enum que puede recuperar valores Y nombres en O (1). También puede generar una matriz de objetos que contiene todos los nombres y valores.
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);
Puedes iniciarlo así:
var enum1 = new Enum({
item1: 0,
item2: 1,
item3: 2
});
Para obtener un valor (como Enums en C #):
var val2 = enum1.item2;
Para obtener un nombre para un valor (puede ser ambiguo al poner el mismo valor para diferentes nombres):
var name1 = enum1.GetName(0); // "item1"
Para obtener una matriz con cada nombre y valor en un objeto:
var arr = enum1.GetObjArr();
Generará:
[{ Name: "item1", Value: 0}, { ... }, ... ]
También puede obtener las opciones de selección de html fácilmente:
var html = enum1.GetSelectOptionsHTML();
Que sostiene:
"<option value=''0''>item1</option>..."
IE8 no admite el método de congelación ().
Fuente: http://kangax.github.io/compat-table/es5/ , haga clic en "¿Mostrar navegadores obsoletos?" en la parte superior, y compruebe IE8 y congelar la intersección de la fila de la fila.
En mi proyecto de juego actual, lo he usado a continuación, ya que pocos clientes siguen usando IE8:
var CONST_WILD_TYPES = {
REGULAR: ''REGULAR'',
EXPANDING: ''EXPANDING'',
STICKY: ''STICKY'',
SHIFTING: ''SHIFTING''
};
También podríamos hacer:
var CONST_WILD_TYPES = {
REGULAR: ''RE'',
EXPANDING: ''EX'',
STICKY: ''ST'',
SHIFTING: ''SH''
};
o incluso esto:
var CONST_WILD_TYPES = {
REGULAR: ''1'',
EXPANDING: ''2'',
STICKY: ''3'',
SHIFTING: ''4''
};
El último, parece más eficiente para la cadena, reduce su ancho de banda total si tiene un servidor y un cliente que intercambian estos datos.
Por supuesto, ahora es su deber asegurarse de que no haya conflictos en los datos (RE, EX, etc. deben ser únicos, también 1, 2, etc. deben ser únicos). Tenga en cuenta que debe mantener estos para siempre para la compatibilidad con versiones anteriores.
Asignación:
var wildType = CONST_WILD_TYPES.REGULAR;
Comparacion
if (wildType === CONST_WILD_TYPES.REGULAR) {
// do something here
}
Se me ocurrió implemented enfoque que se basa en las enumeraciones en Java. Estos son de tipo seguro, por lo que también puede realizar instanceof
comprobaciones.
Puedes definir enumeraciones como esta:
var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);
Days
ahora se refiere a la Days
enumeración:
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"
La implementación:
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
}
})();
Si está utilizando Backbone , puede obtener la funcionalidad de enumeración completa (buscar por id, nombre, miembros personalizados) de forma gratuita con 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 ColorEnum = {
red: {},
green: {},
blue: {}
}
No necesita asegurarse de no asignar números duplicados a diferentes valores de enumeración de esta manera. Un nuevo objeto se crea una instancia y se asigna a todos los valores de enumeración.