¿Cuál es la sintaxis preferida para definir enumeraciones en JavaScript?

2082

¿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?

David Citron
fuente
132
No lo use 0como número de enumeración. A menos que se use para algo que no se haya configurado. JS trata a false || undefined || null || 0 || "" || '' || NaNtodos como el mismo valor en comparación con el uso ==.
matsko
153
@matsko no es solo un argumento en contra de usar ==?
sdm350
66
0 == nulldevuelve falso
mcont
11
Pero false == 0y +null == 0(y las conversiones a números suceden a veces cuando no lo espera), mientras que null == undefinedtambién, y +undefinedes NaN(aunque NaN != NaN).
sanderd17
46
La matriz de doble igualdad es más confusa que el formato automático de Microsoft Word
aaaaaa

Respuestas:

896

Desde 1.8.5 es posible sellar y congelar el objeto, así que defina lo anterior como:

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

o

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

¡y voilá! JS enumeraciones.

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 utilizar una herramienta como TypeScript o Flow .

Fuente

No se necesitan citas, pero las conservé para mantener la coherencia.

Artur Czajka
fuente
66
Según Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ) es aplicable a Firefox 4, IE 9, Opera 11.60 y sé que funciona en Chrome.
Artur Czajka
77
Esta es la respuesta ahora mismo en 2012. Más simple: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. No necesita especificar una identificación, solo puede usar un objeto vacío para comparar enumeraciones. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas
34
Por razones de compatibilidad,if (Object.freeze) { Object.freeze(DaysEnum); }
saluce
17
Me gustaría señalar que hacer ({ monday: {}, etc. significa que si convierte ese objeto a JSON a través de stringify obtendrá lo [{"day": {}}]que no funcionará.
jcollum
10
@Supuhstar Mi opinión sobre esta pregunta ahora es diferente. No use freeze (), es completamente inútil y una pérdida de tiempo haciendo cosas "estúpidas". Si desea exponer una enumeración, sólo tiene que exponer a esto: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Comparar objetos como en mi comentario anterior es MUCHO MÁS LENTO que comparar números.
Gabriel Llamas
608

Esta no es una gran respuesta, pero yo diría que funciona bien, personalmente.

Habiendo dicho eso, dado que no importa cuáles sean los valores (ha usado 0, 1, 2), usaría una cadena significativa en caso de que alguna vez quisiera generar el valor actual.

Gareth
fuente
377
Esto se indicó en otra respuesta, pero como esta es la respuesta aceptada, publicaré esto aquí. La solución del OP es correcta. Sin embargo, será aún mejor si se usa con Object.freeze(). Esto evitará que otro código cambie los valores de la enumeración. Ejemplo:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth
55
@TolgaE gracias por esa biblioteca! ¡Me inspiró no solo a reducirlo al mínimo, sino también a agregar un par de funciones! Bifurqué el tuyo y lo puse todo aquí: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar
3
@Supuhstar ¡Eso es genial! Me alegra que pueda usarlo. Siéntase libre de hacer una solicitud de extracción si desea fusionarla en esta biblioteca, luego puedo actualizar la biblioteca npm
Tolga E
2
Si alguien está interesado, he implementado enumeraciones de tipo seguro de forma similar a como están en Java. Esto significa que puedes hacer instanceofcheques. Por ejemplo ColorEnum.RED instanceof ColorEnum(devoluciones true). También puede resolver una instancia de un nombre ColorEnum.fromName("RED") === ColorEnum.RED(devoluciones true). Cada instancia también tiene un .name()y un .ordinal()método, y la enumeración en sí tiene un values()método que devuelve una matriz de todas las constantes.
Vivin Paliath
3
No estoy seguro de estar de acuerdo con la sugerencia de "cadena significativa". Las enumeraciones no deben considerarse como cadenas o números; Son tipos de datos abstractos. No debería ser posible "generar el valor actual" sin algún método auxiliar. En Java y .NET, es el ToString()método. ¡Los desarrolladores de JS ya somos demasiado dependientes de las cosas que "simplemente funcionan"! Además, uno debería poder rápidamente switchen una enumeración. Comparar cadenas es más lento que comparar números, por lo que obtendrá un switchrendimiento ligeramente peor si usa cadenas en lugar de números enteros.
Rabadash8820
501

ACTUALIZAR

Gracias por todos los votos a favor de todos, pero no creo que mi respuesta a continuación sea la mejor manera de escribir enumeraciones en JavaScript. Vea mi publicación de 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, puede hacer que los valores sean objetos, para que pueda tener el pastel y comérselo 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í por conveniencia. Además, el nombre de la propiedad de tamaño en sí no necesita ser codificado, sino que también se puede establecer dinámicamente. Entonces, suponiendo que solo conozca 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 algunas suposiciones ya no se pueden hacer (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 de antemano mucho sobre ellos.

Ejemplo

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, puede echar un vistazo a mi solución para la administración de dependencias y espacios de nombres simples pero potentes para JavaScript: Paquetes JS

Stijn de Witt
fuente
Entonces, ¿cómo irías y crearías simplemente un TAMAÑO si solo tienes su nombre?
Johanisma
2
@Johanisma: Ese caso de uso realmente no tiene sentido para las enumeraciones, ya que la idea de ellas es que conoces todos los valores de antemano. Sin embargo, no hay nada que le impida agregar valores adicionales más adelante en Javascript. Agregaré un ejemplo de eso a mi respuesta.
Stijn de Witt
2
+1 para el enlace a su publicación con el enfoque de propiedades. Elegante en que las declaraciones básicas son simples, como en el OP, con la característica de propiedades agregadas cuando se desea.
goodeye
@Stijin, me gustó mucho tu solución actualizada. Código publicado en comentarios en su blog y como comentario a continuación. Básicamente, utilizando una función, realice la compilación de propiedades a partir de una lista hash existente y, opcionalmente, inmovilícela (mkenum_2 en mi lista). Salud.
Andrew Philips
También hay una biblioteca que lo implementa, que también incluye buenas características en comparación y búsqueda inversa: github.com/adrai/enum
Roman M
83

En pocas palabras: no puedes.

Puedes fingirlo, pero no obtendrás seguridad de tipografía. 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 de enumerador duplicados. Por ejemplo:

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

Editar

¿Qué pasa con la congelación de objetos de Artur Czajka? ¿No funcionaría eso para evitar que establezcas de lunes a jueves? - Fry Quad

Absolutamente, Object.freezesolucionaría totalmente el problema del que me quejé. Me gustaría recordarles a todos que cuando escribí lo anterior, Object.freezerealmente no existía.

Ahora ... ahora abre algunas posibilidades muy interesantes.

Edición 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 ajuste a todos los usos válidos de enumeraciones, es muy útil.

Randolpho
fuente
103
hay seguridad de tipo en javascript?
Scott Evernden
3
Por lo tanto, no asigne valores a las propiedades del objeto. Use getter para acceder al enumerante (almacenado como una propiedad de, por ejemplo, un objeto "privado"). Una implementación ingenua se vería como:var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax
2
@ Scott Evernden: punto tomado. @kangax: el punto es que todavía es un truco. Las enumeraciones simplemente no existen en Javascript, punto, final de la historia. Incluso el patrón sugerido por Tim Sylvester sigue siendo un truco menos que ideal.
Randolpho
2
Rociar el código con literales no es muy fácil de mantener, por lo que tiene sentido crear constantes para él. Por supuesto, Javascript tampoco tiene constantes. Básicamente, esta es solo una forma de escribir código limpio. No se puede hacer cumplir, pero no mucho en Javascript. Puede redefinir constantes, funciones o, en su mayoría, cualquier cosa. Por ejemplo: document.getElementById = function () {alert ("Estás jodido. Javascript no es seguro para los tipos");};
Stijn de Witt
3
@Randolpho: ¿Qué pasa con la congelación de objetos de Artur Czajka? ¿No funcionaría eso para evitar que establezcas de lunes a jueves?
Michael - ¿Dónde está Clay Shirky?
56

Esto es lo que todos queremos:

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

Ahora puedes crear tus enumeraciones:

var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);

Al hacer esto, las constantes se pueden acceder de la manera habitual (Sí No, SÍ, Color.GREEN) y obtienen un valor int secuencial (NO = 0, YES = 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 */
};


Editar - pequeña mejora - ahora con varargs: (desafortunadamente no funciona correctamente en IE: S ... entonces 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');
Andre 'Fi'
fuente
Me encanta la simplicidad de esta respuesta!
Marquizzo
@Marquizzo (y OP) Creé una versión mejorada basada en esta respuesta: stackoverflow.com/a/60309416/1599699
Andrew
53

En la mayoría de los navegadores modernos, hay un tipo de datos primitivo de símbolo que se puede usar para crear una enumeración. Garantizará la seguridad de tipo de la enumeración, ya que JavaScript garantiza que cada valor de símbolo sea ú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")});

Demo de Plunker

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
Vitalii Fedorenko
fuente
Esta es la respuesta correcta en teoría. En la práctica, el soporte del navegador 2015 está lejos de ser suficiente. La producción no está lista de lejos.
vbraun
1
Aunque el soporte del navegador aún no existe, esta es la mejor respuesta, ya que está cerca de lo que Symbolestá destinado.
rvighne
2
Sin embargo, los valores de Meh ... enum a menudo deben ser serializables, y los símbolos no son tan útiles para serializar y deserializar.
Andy
3
¿Soy solo yo o es Object.freezesolo para personas que no han aceptado el hecho de que "monkeypatch bajo su propio riesgo" es el contrato social de JS?
Andy
@ Andy, la serialización es molesta. Terminé haciendo un explícito toJSONen la clase que contiene para usar este enfoque: stackoverflow.com/questions/58499828/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
30

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

Veamos directamente el problema: tamaño del archivo. Todas las demás respuestas enumeradas aquí hinchan su código al extremo. Le presento que para obtener el mejor rendimiento posible, legibilidad del código, administración de proyectos a gran escala, sugerencias de sintaxis en muchos editores de código y reducción del tamaño del código por minificación, esta es la forma correcta de hacer enumeraciones: variables de notación de subrayado.


wwwwwwwwww

Variables de notación de subrayado

Como se muestra en el cuadro anterior y en el ejemplo a continuación, aquí hay cinco pasos sencillos para comenzar:

  1. Determine un nombre para el grupo de enumeración. Piense en un sustantivo que pueda describir el propósito de la enumeración o al menos las entradas en la enumeración. Por ejemplo, un grupo de enumeraciones que representan colores elegibles por el usuario podría denominarse mejor COLORCHOICES que COLORS.
  2. Decida si las enumeraciones en el grupo son mutuamente excluyentes o independientes. Si se excluyen mutuamente, comience cada nombre de variable enumerado con ENUM_. Si es independiente o de lado a lado, useINDEX_ .
  3. Para cada entrada, cree una nueva variable local cuyo nombre comience con ENUM_oINDEX_ , luego el nombre del grupo, luego un guión bajo y luego un nombre descriptivo único para la propiedad
  4. Añada ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, o INDEXLEN_(si LEN_oLENGTH_ es preferencia personal) variables enumeradas al final. Debe usar esta variable siempre que sea posible en su código para asegurarse de que agregar una entrada adicional a la enumeración e incrementar este valor no rompa su código.
  5. Dar cada variable enumerado sucesiva un valor uno más que el último, a partir de 0. Hay comentarios sobre esta página que digamos 0no debe ser utilizado como un valor enumerado porque 0 == null, 0 == false, 0 == "", y otra locura JS. Le presento que, para evitar este problema y aumentar el rendimiento al mismo tiempo, use siempre ===y nunca deje que ==aparezca en su código, excepto con typeof(ex typeof X == "string"). En todos mis años de uso ===, nunca he tenido un problema con el uso de 0 como valor de enumeración. Si todavía es aprensivo, 1podría usarse como valor inicial en ENUM_enumeraciones (pero no en INDEX_enumeraciones) sin penalización de rendimiento en muchos casos.
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

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

Así es como recuerdo cuándo usarlo INDEX_y cuándo usarlo ENUM_:

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

Sin embargo, ENUM_puede, en ciertas circunstancias, ser apropiado como un índice, como al contar las ocurrencias de cada elemento.

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

Observe que, en el código anterior, es realmente fácil agregar un nuevo tipo de mascota: solo tendría que agregar una nueva entrada después ENUM_PET_RATy actualizar en ENUMLEN_PETconsecuencia. Puede ser más difícil y con errores agregar una nueva entrada en otros sistemas de enumeración.


No de lo que ... ... ¡... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡...

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

Además, esta sintaxis de enumeraciones permite una clase clara y concisa que se extiende como se ve a continuación. Para extender una clase, agregue un número incremental a la LEN_entrada de la clase primaria. Luego, finalice la subclase con su propia LEN_entrada para que la subclase se pueda extender aún más en el futuro.

Diagrama de extensión adicional

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

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

(Longitud: 2,450 bytes)

Algunos pueden decir que esto es menos práctico que otras soluciones: quita 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 sin minificar 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 aquí . El compilador de cierre puede tomar todos estos datos de enumeración e incorporarlos, lo que hace que su Javascript sea súper pequeño y se ejecute súper rápido. Por lo tanto, Minify with Closure Compiler. Observar.


No de lo que ... ... ¡... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡...

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

El compilador de cierre puede realizar algunas optimizaciones bastante increíbles a través de inferencias que están mucho más allá de las capacidades de cualquier otro minificador de Javascript. Closure Compiler es capaz de incorporar variables primitivas en un valor fijo. Closure Compiler también puede hacer inferencias basadas en estos valores en línea y eliminar bloques no utilizados en sentencias if y bucles.

Código de escurrido a través del compilador de cierre

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

(Longitud: 605 bytes)

Closure Compiler lo recompensa por codificar de manera más inteligente y organizar bien su código porque, mientras que muchos minificadores castigan el código organizado con un tamaño de archivo minificado más grande, Closure Compiler puede filtrar toda su limpieza y cordura para generar un tamaño de archivo aún más pequeño si usa trucos como enumeraciones de nombres de variables. Eso, en esta mente, es el santo grial de la codificación: una herramienta que ayuda tanto a su código con un tamaño reducido más pequeño como a su mente al entrenar mejores hábitos de programación.


No de lo que ... ... ¡... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡...

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

Ahora, veamos qué tan grande sería el archivo equivalente sin ninguna de estas enumeraciones.

Origen sin usar enumeraciones (longitud: 1,973 bytes (¡477 bytes más corto que el código enumerado!))
Minificado sin usar enumeraciones (longitud: 843 bytes (238 bytes más que el código enumerado ))

Tabla de tamaños de código



Como se ve, sin enumeraciones, el código fuente es más corto a costa 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.


No de lo que ... ... ¡... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡...

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

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 de código minimizado. Cuando se trabaja en un proyecto grande con muchas otras personas, puede ser beneficioso marcar y etiquetar explícitamente los nombres de las variables con quién creó el código para que el creador original del código pueda identificarse rápidamente para la corrección colaborativa de errores.

// 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 minificación. En las propiedades con nombre normal, el navegador tiene que usar hashmaps para buscar dónde está la propiedad en el objeto. Aunque los compiladores JIT almacenan en caché de forma inteligente esta ubicación en el objeto, todavía hay una sobrecarga tremenda debido a casos especiales como la eliminación de una propiedad inferior del objeto.

Pero, con arreglos PACKED_ELEMENTS indexados enteros no dispersos continuos , el navegador puede omitir gran parte de esa sobrecarga porque el índice del valor en el arreglo interno ya está especificado. Sí, de acuerdo con el estándar ECMAScript, se supone que 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.

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

Compare el código de arriba con el código de abajo.

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

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

Uno podría objetar el código con enumeraciones que parecen ser mucho más largas que el código con objetos ordinarios, pero la apariencia puede ser engañosa. Es importante recordar que el tamaño del código fuente no es proporcional al tamaño de salida cuando se utiliza el compilador de cierre épico. Observar.

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

El código minificado sin enumeraciones está arriba y el código minificado con enumeraciones está abajo.

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

El ejemplo anterior demuestra que, además de tener un rendimiento superior, el código enumerado también da como resultado un tamaño de archivo minimizado más pequeño.


No de lo que ... ... ¡... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡...

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

Además, la cereza personal de este en la parte superior está utilizando esta forma de enumeraciones junto con el editor de texto CodeMirror en modo Javascript. El modo de resaltado de sintaxis Javascript de CodeMirror resalta las variables locales en el alcance actual. De esa manera, usted sabe instantáneamente cuando escribe un nombre de variable correctamente porque si el nombre de la variable se declaró previamente con la excepción al ejecutar código con nombres de enumeración mal escritos. Además, las herramientas de JavaScript, como JSLint y Closure Compiler, son muy ruidosas al decirle cuando escribe incorrectamente en 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.var palabra clave, entonces el nombre de la variable cambiará a un color especial (cian por defecto). Incluso si no utiliza CodeMirror, al menos el navegador arroja un útil[variable name] is not defined

CodeMirror destacando demostración

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

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

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

En el fragmento anterior, se le alertó con un error porque ENUM_COLORENUM_DNEno existe.


No de lo que ... ... ¡... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡...

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Creo que es seguro decir que esta metodología de enumeración es, de hecho, la mejor manera de ir no solo para el tamaño de código minimizado, sino también para el rendimiento, la depuración y la colaboración.

Después de leer una pregunta útil, agradezco al autor por dedicar tiempo a su escritura haciendo clic en la flecha superior izquierda hacia arriba en el cuadro de preguntas. Cada cuadro de respuesta también tiene una de estas flechas hacia arriba.

Jack Giffin
fuente
Eh Prefiero la legibilidad y facilidad de uso y comprensión al tamaño del código.
Andrew
1
@ Andrew Con mi respuesta, puedes tener ambos. Mi respuesta da como resultado el código más fácil de usar / administrar y el tamaño de código minificado más pequeño. ”
Jack Giffin
1
@ Andrew He intentado aplicar su Yet Another Enum (¡YEA!) Al ejemplo del analizador de color en mi respuesta. Sin embargo, he encontrado varios problemas que es posible que desee abordar. YEA no tiene forma de extender las enumeraciones con subclases, lo que me obliga a crear clases separadas para padres e hijos, lo que podría ser bastante difícil de administrar en proyectos grandes. YEA no garantiza que la entrada exista (ex colors.REEDrendimientos undefined), por lo que los errores tipográficos crean acertijos escurridizos. YEA no distingue entre el uso de enumeraciones como índices e ID, lo que lleva a un código confuso donde todo se ve igual. ...
Jack Giffin
1
@ Andrew ... YEA obstaculiza la capacidad del compilador de cierre para minificar. Compare el código fuente con YEA (3549 bytes) con el código minificado con YEA (1344 bytes) con el código minificado con mi solución (604 bytes). Finalmente, YEA implica "mapeo por nombre" porque separa los nombres de cadena de las ID enumeradas. El mío solo considera la identificación, por lo que no se necesita "mapeo por nombre", lo que lleva a un diseño más simple y un mejor rendimiento. Gracias por compartir su solución, pero necesita muchas soluciones antes de que pueda ser práctica.
Jack Giffin
1
@ Andrew Tienes derecho a tu opinión como yo a la mía 👍
Jack Giffin
23

He estado jugando con esto, ya que amo mis enumeraciones. =)

Usando Object.definePropertycreo que se me ocurrió una solución algo 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, writable:falseesto debería hacer que sea seguro.

Por lo tanto, debería poder crear un objeto personalizado y luego invocarlo Enum(). Los valores asignados comienzan en 0 y se incrementan por elemento.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3
Duncan
fuente
3
Si agrega return this;al final de Enum, podría hacer:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP
No lo consideré, ya que no es mi método normal de hacer las cosas. ¡Pero tienes toda la razón! Lo editaré en.
Duncan
Realmente me gusta esto, aunque no soy un gran fanático de acumular espacio de objetos (con la función global ENUM). Convirtió esto en una función mkenum y agregó asignaciones numéricas opcionales => var mixedUp = mkenum ('NEGRO', {ROJO: 0x0F00, AZUL: 0X0F, VERDE: 0x0F0, BLANCO: 0x0FFF, UNO: 1}, DOS, TRES, CUATRO) ; // Agregar mi código como respuesta a continuación. Gracias.
Andrew Philips
Para ser honesto, ya ni siquiera uso esto. He estado usando el Compilador de cierre de Google, y esto no funciona demasiado bien (o simplemente complica las cosas) si usa la configuración Avanzado. Así que acabo de volver a la notación de objetos estándar.
Duncan
1
false es el valor predeterminado para writable , enumerabley configurable. No hay necesidad de masticar los valores predeterminados.
ceving
23

Usar proxies Javascript

TLDR: agregue esta clase a sus métodos de utilidad y úsela en todo su código, se burla del comportamiento de Enum de los lenguajes de programación tradicionales y en realidad arroja errores cuando intenta acceder a un enumerador que no existe o agregar / actualizar un enumerador. No hay necesidad de confiar 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 cree enumeraciones instanciando la clase:

const roles = new Enum({
  ADMIN: 'Admin',
  USER: 'User',
});

Explicación completa:

Una característica muy beneficiosa de Enums que obtienes de los idiomas tradicionales es que explotan (arrojan 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 burlada para evitar que se agreguen valores adicionales accidental / maliciosamente, ninguna de las otras respuestas aborda esa característica intrínseca de Enums.

Como probablemente sepa, acceder a miembros no existentes en JavaScript simplemente devuelve undefinedy no explota su código. Como los enumeradores son constantes predefinidas (es decir, días de la semana), nunca debería haber un caso en el que un enumerador no esté definido.

No me malinterpreten, el comportamiento de JavaScript de regresar undefinedal acceder a propiedades indefinidas es en realidad una característica muy poderosa del lenguaje, pero no es una característica que desea cuando intenta burlarse de las estructuras tradicionales de Enum.

Aquí es donde brillan los objetos proxy. Los proxies se estandarizaron en el lenguaje con la introducción de ES6 (ES2015). Aquí está la descripción de MDN:

El objeto Proxy se usa para definir comportamientos personalizados 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 proxys de JavaScript pueden interceptar operaciones en objetos (con el uso de "trampas", llámelos ganchos si lo desea) y le permiten realizar varias comprobaciones, acciones y / o manipulaciones antes de que se completen (o en algunos casos, detener por completo las operaciones, que es exactamente lo que queremos hacer si tratamos de hacer referencia a un enumerador que no existe).

Aquí hay un ejemplo artificial 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"


Aparte: ¿Qué diablos es un proxy?

Recuerdo que cuando comencé a ver la palabra proxy en todas partes, definitivamente no tenía sentido para mí por mucho tiempo. Si ese es usted en este momento, creo que una forma fácil de generalizar proxies es pensar en ellos como software, instituciones o incluso personas que actúan como intermediarios o intermediarios entre dos servidores, empresas o personas.

Govind Rai
fuente
¿Cómo hacer algo como myEnum.valueOf ("someStringValue")? Esperado: en caso de que la cadena de entrada tenga un valor de un elemento del enumerador, debe devolver el elemento. En caso de que ningún elemento tenga ese valor de cadena, lanza una excepción.
sscarduzio
@sscarduzio puede anular el valueOfmétodo predeterminado al especificarlo como método de instancia en la clase Enum. Sin embargo, ¿por qué quiere acceder de esta manera en lugar de simplemente acceder a él mediante la notación de puntos?
Govind Rai
Mi enumeración es const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}) y analizo desde la entrada una cadena arbitraria "info" o "debug". Así que necesito algo como currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio
1
¿Por qué no pudiste hacer logLevelEnum[settings.get("log_level")]? agregar parseOrThrowsolo sería repetitivo a lo que las trampas de proxy ya están haciendo por usted.
Govind Rai
17

Este es uno antiguo que conozco, pero la forma en que se ha implementado a través de la interfaz 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 lo MyEnum.Barque devuelve 1 como en lo MyEnum[1]que devuelve "Barra" independientemente del orden de la declaración.

Rob Hardy
fuente
1
Además, MyEnum ["Bar"] funciona y devuelve 1 ... <3 TypeScript hasta ahora ...
David Karlaš
3
y, por supuesto, si en realidad ESTÁS usando Typecript:enum MyEnum { Foo, Bar, Foobar }
parlamento
16

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 usar la clase en lugar del objeto literal) es tener una clase principal, Enumentonces todas las enumeraciones extenderán esa clase.

 class ColorEnum  extends Enum {/*....*/}
Abdennour TOUMI
fuente
44
¿Podría explicar por qué tener una clase para padres es una ventaja, por favor? ¡Siento que me falta algo!
Jon G
77
No hagas eso. new ColorEnum()No tiene absolutamente ningún sentido.
Bergi
3
extendiéndose una enumeración suena loco, de verdad
Codii
una vez que el lenguaje no lo soporte de forma nativa, ¡tendría sentido mantener esta convención y usarla así! ¡estoy de acuerdo!
xpto
Creo que (?) A lo que se refiere OP es: El beneficio de la estática pura es que está disponible en todas partes como un singleton, y no necesita crear una instancia de la clase, ¡OP no sugiere que lo haga! Creo que lo que está diciendo es que la superclase Enumtiene estándar estática métodos empadronador en él, como getValues(), getNames(), iterate(), etc. Si ese es el caso, usted no tiene que volver a implementar los mismos en cada nuevo tipo de enum.
Ingeniero
15

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 define sus enumeraciones 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:

  • Enums fáciles de declarar
  • Fácil acceso a tus enumeraciones
  • Tus enumeraciones pueden ser tipos complejos
  • La clase Enum tiene algo de almacenamiento en caché asociativo si usa mucho getByValue

Algunas desventajas:

  • Hay algo de administración de memoria desordenada, ya que mantengo las referencias a las enumeraciones
  • Todavía no hay seguridad de tipo
Chris
fuente
14

Crear un objeto literal:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
hvdd
fuente
12
constno hace que las propiedades del objeto sean inmutables, solo significa que la variable Modesno se puede reasignar a otra cosa. Para hacerlo más completo, úsalo Object.freeze()junto const.
rvighne
Por favor no lo use Object.freeze. Impide que Closure Compiler forre el objeto.
Jack Giffin
11

Si está utilizando Backbone , puede obtener una funcionalidad de enumeración completa (buscar por id, nombre, miembros personalizados) de forma gratuita mediante 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()
Yaroslav
fuente
8

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
Xeltor
fuente
1
@JackGiffin Estoy de acuerdo en que su respuesta es más eficaz y que la mía puede requerir más memoria, aunque no debe suponer que todos quieren una enumeración de la manera en que C ++ la implementó. Respete otras respuestas y los desarrolladores que podrían preferir esta sobre la suya.
Xeltor
7

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
David Miró
fuente
6

Se me ocurrió este enfoque, que se basa en las enumeraciones en Java. Estos son de tipo seguro, por lo que también puede realizar instanceofverificaciones.

Puede definir enumeraciones como esta:

var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);

Daysahora se refiere a la Daysenumeració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
    }

})();
Vivin Paliath
fuente
Se ve bien, ¿tal vez debería verificar la existencia del freezemétodo para la compatibilidad con versiones anteriores? Por ejemplo,if (Object.freeze) { Object.freeze(values); }
FBB
¡Buen punto! Lo haré!
Vivin Paliath
6

IE8 no es compatible con el método freeze ().
Fuente: http://kangax.github.io/compat-table/es5/ , haga clic en "¿Mostrar navegadores obsoletos?" en la parte superior y verifique IE8 y congele la intersección de la fila de columnas.

En mi proyecto de juego actual, lo he usado a continuación, ya que pocos clientes aún usan 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 mantenerlos 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
}
Manohar Reddy Poreddy
fuente
5
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 instancia y se asigna a todos los valores de enumeración.

Shivanshu Goyal
fuente
Esta respuesta está subestimada. Es una de mis ideas favoritas por su simplicidad. En la práctica, creo que seguiré las cadenas porque es más fácil de depurar por ahora.
Domino
Hmm, solo asegúrate de que este código no sea llamado dos veces ...
Andrew
4

Aquí hay un par de formas diferentes de implementar enumeraciones TypeScript .

La forma más fácil es simplemente iterar sobre un objeto, agregando pares 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 lodash mixin para crear una enumeración usando una cadena. Si bien esta versión es un poco más complicada, hace la numeración automáticamente por usted. Todos los métodos 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
Blake Bowen
fuente
muy inteligente, gracias
Ilan
4

Acabo de publicar un paquete NPM gen_enum le permite crear una estructura de datos de 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, consulte https://github.com/greenlaw110/enumjs

Actualizaciones

Obtuve el paquete obsoleto gen_enumy fusiono la función en el paquete constjs , que proporciona más funciones, incluidos objetos inmutables, deserialización de cadenas JSON, constantes de cadenas y generación de mapas de bits, etc. Consulte https://www.npmjs.com/package/constjs para obtener más información

Para actualizar desde gen_enumque constjsacaba de cambiar la declaración

var genEnum = require('gen_enum');

a

var genEnum = require('constjs').enum;
Gelin Luo
fuente
4

La solución más simple:

Crear

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

Obtenga valor

console.log(Status.Ready) // 1

Obtener la clave

console.log(Object.keys(Status)[Status.Ready]) // Ready
Ilya Gazman
fuente
4

He creado una clase Enum que puede obtener 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 buscar 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 html fácilmente:

var html = enum1.GetSelectOptionsHTML();

Que tiene:

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

A pesar de que solo los métodos estáticos (y no las propiedades estáticas) son compatibles con ES2015 (consulte aquí también, §15.2.2.2), curiosamente puede usar lo siguiente con Babel con el valor es2015predeterminado:

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 en todos los módulos (por ejemplo, importar la CellStateenumeración de otro módulo) y también cuando importo un módulo usando Webpack.

La ventaja que tiene este método 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 usando la verificación de tipo estático, que sus variables, parámetros, etc. son específicosCellState " enum "en lugar de algún otro enum (que sería imposible de distinguir si utilizara objetos o símbolos genéricos).

actualizar

El código anterior tiene una deficiencia, ya que le permite a uno crear objetos de tipo adicionales CellState(a pesar de que uno no puede asignarlos a los campos estáticos CellStateya que está congelado). Aún así, el siguiente código más refinado ofrece las siguientes ventajas:

  1. no más objetos de tipo CellState se pueden crear
  2. tiene garantizado que no hay dos instancias enum asignadas al mismo código
  3. método de utilidad para recuperar la enumeración de una representación de cadena
  4. la valuesfunció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;
Marcus Junius Brutus
fuente
Buen ejemplo :-)
Ashraf.Shk786
4

es7 way, (iterador, congelación), uso:

const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')

for (let name of ThreeWiseMen)
    console.log(name)


// with a given key
let key = ThreeWiseMen.Melchior

console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)

for (let entry from key.enum)
     console.log(entry)


// prevent alteration (throws TypeError in strict mode)
ThreeWiseMen.Me = 'Me too!'
ThreeWiseMen.Melchior.name = 'Foo'

código:

class EnumKey {

    constructor(props) { Object.freeze(Object.assign(this, props)) }

    toString() { return this.name }

}

export class Enum {

    constructor(...keys) {

        for (let [index, key] of keys.entries()) {

            Object.defineProperty(this, key, {

                value: new EnumKey({ name:key, index, enum:this }),
                enumerable: true,

            })

        }

        Object.freeze(this)

    }

    *[Symbol.iterator]() {

        for (let key of Object.keys(this))
            yield this[key]

    }

    toString() { return [...this].join(', ') }

}
Joseph Merdrignac
fuente
4

Así es como mecanografía se traduce enumen 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]regresa '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
Julius Dzidzevičius
fuente
4

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

LNT
fuente
3

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"
usuario2254487
fuente
66
La función es innecesaria y le da exactamente el mismo resultado que el OP publicado.
Sildoreth
3

Al momento de escribir, octubre de 2014 , 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, así como subrayar JS. Puede ignorarlos fácilmente y simplemente tomar el código Enum si lo prefiere.

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 constantes enteras. Y puede reutilizar la fábrica con diferentes valores y exportar los Enums generados utilizando el enfoque modular de Node.

Una vez más, si solo es un usuario ocasional, o en el navegador, etc., simplemente tome la parte de fábrica del código, y también puede eliminar la biblioteca de subrayado si no desea usarlo en su código.

arcseldon
fuente
55
¿Podría publicar una respuesta con solo "aquí se explica cómo hacer esto como un usuario casual que solo quiere enumeraciones, no fábricas, guiones bajos o algo elegante"?
GreenAsJade
55
Aunque esto es bastante impresionante desde el punto de vista de los desarrolladores, no es muy limpio o legible. La solución Enum del OP es más fácil y más legible en todos los sentidos, y por lo tanto, es mejor usarla. Aún así, es increíble que se te ocurriera esto.
David
3

Es fácil de usar, creo. 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);

ACTUALIZAR:

Hay mis códigos de ayuda ( TypeHelper).

Sherali Turdiyev
fuente