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


¿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 una expresión más preferible?

Author: Flip, 2008-11-13

30 answers

Desde la versión 1.8.5 es posible sellar y congelar el objeto, así que define 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 voila! JS enums.

Sin embargo, esto no le impide asignar un valor no deseado a una variable, que a menudo es el objetivo principal de enums:

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

Una forma de garantizar un mayor grado de seguridad de tipo (con enums o de otra manera) es usar una herramienta como TypeScript o Flow.

Fuente

Las citas no son necesario, pero los guardé para la consistencia.

 583
Author: Artur Czajka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-25 00:04:55

Esto no es una gran respuesta, pero yo diría que funciona muy bien, personalmente

Dicho esto, ya 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.

 585
Author: Gareth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-13 19:22:44

UPDATE : Gracias por todos los votos positivos, pero no creo que mi respuesta a continuación sea la mejor manera de escribir enumeraciones en Javascript. Ver mi entrada de blog para más detalles: Enumeraciones 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 comerlo 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 tarde:

// 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 el control de identidad y solo están allí por conveniencia. Además, el nombre de la propiedad size en sí no necesita ser codificado, sino que también se puede establecer dinámicamente. Así que suponiendo que solo conoce el nombre de su nuevo valor de enumeración, todavía 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 la correcta orden 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. Usted puede hacer un bucle a través de ellos o de otra manera manipularlos sin saber mucho sobre ellos de antemano.

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 la gestión de espacios de nombres y dependencias simple pero potente para javascript: Paquetes JS

 480
Author: Stijn de Witt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-09 18:41:26

En pocas palabras: No puedes.

Puede fingir, pero no obtendrá seguridad de tipo. 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 tener accidentalmente valores de enumerante duplicados. Por ejemplo:

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

Editar

Qué pasa con el Objeto de Artur Czajka.freeze? ¿No funcionaría eso para evitar que ¿de lunes a jueves? - Fry Quad

Absolutamente, Object.freeze solucionaría totalmente el problema del que me quejé. Me gustaría recordar a todos que cuando escribí lo anterior, Object.freeze realmente no existía.

Ahora.... ahora abre algunas posibilidades muy interesantes.

Editar 2
Aquí hay una biblioteca muy buena para crear enumeraciones.

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

Mientras que probablemente no se adapta a cada uso válido de enumeraciones, va un camino muy largo.

 80
Author: Randolpho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-06 16:09:02

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 (YesNo.SÍ, Color.VERDE) y obtienen un valor secuencial int (NO = 0 , SÍ = 1; ROJO = 0, VERDE = 1, AZUL = 2).

También puede agregar métodos, utilizando Enum.prototipo:

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... debe seguir con la versión anterior entonces)

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

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');
 46
Author: Andre 'Fi',
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-19 01:21:59

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

Plunker demo

En GitHub puedes encontrar un wrapper que simplifica el código requerido para inicializar el enum:

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

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE
 44
Author: Vitalii Fedorenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-31 23:33:29

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

Usando Object.defineProperty Creo que se me ocurrió una solución algo viable.

Aquí hay un jsfiddle: http://jsfiddle.net/ZV4A6 /

Usando este método.. usted debería (en teoría) ser capaz de 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:false este debería hacerlo seguro de tipo.

Así que usted debe ser capaz de cree un objeto personalizado, luego llame a Enum() en él. Los valores asignados comienzan en 0 e incrementan por elemento.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3
 23
Author: Duncan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-21 17:20:55

Este es uno antiguo que conozco, pero la forma en que se ha implementado desde entonces 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 MyEnum.Bar que devuelve 1, como MyEnum[1] que devuelve "Bar" independientemente del orden de la declaración.

 18
Author: Rob Hardy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-24 16:11:14

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 defines tus enums así:

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 enums:

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

Normalmente 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 de acceder a sus enumeraciones
  • Sus enumeraciones pueden ser tipos complejos
  • La clase Enum tiene algún almacenamiento asociativo en caché si está utilizando getByValue a lote

Algunas desventajas:

  • Algún manejo de memoria desordenado pasando allí, ya que guardo las referencias a los enums
  • Todavía no hay seguridad de tipo
 15
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-15 09:31:57

Si está utilizando Backbone, puede obtener la funcionalidad completa de enum (buscar por id, nombre, miembros personalizados) de forma gratuita utilizando Backbone.Colección.

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

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

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

// using
Colors.Red.print()
 12
Author: Yaroslav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-24 14:04:46

En ES7 , puede hacer una ENUMERACIÓN 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 clase en lugar de objeto literal) es tener una clase padre Enum entonces todas sus Enumeraciones extenderán esa clase.

 class ColorEnum  extends Enum {/*....*/}
 12
Author: Abdennour TOUMI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-06 07:07:59

Crear un objeto literal:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
 11
Author: hvdd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-01 17:03:41

La "Sintaxis Perfilada" por la mayoría de las personas ya se ha enumerado anteriormente. Sin embargo, hay un gran problema general: el rendimiento. Ni una sola de las respuestas anteriores es muy eficiente en lo más mínimo y todos hinchan el tamaño de su código al extremo. Para un rendimiento real, facilidad de lectura de código y una reducción sin precedentes en el tamaño del código por minificación, esta es la forma correcta de hacer 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 extensión de clase clara y concisa 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(rawstr) {
        rawstr = rawstr.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

Algunos pueden decir que esto es menos práctico que otras soluciones: renuncia a toneladas de espacio, se necesita mucho tiempo para escribir, y no está recubierto con sintaxis de azúcar. Sí, esas personas sería correcto si no reducen su código. Sin embargo, ninguna persona razonable dejaría código sin minificar en el producto final. Para esta minificación, el Compilador de cierre es el mejor que tengo que encontrar. El acceso en línea se puede encontrar aquí. Closure compiler es capaz de tomar todos estos datos de enumeración e insertarlos, haciendo que su javascript funcione súper rápido y sea súper pequeño. 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 Enumeraciones (longitud: 1.973 bytes (más corto!))
Minificado Sin Enumeraciones (longitud: 843 bytes (más))

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 usted, sé con certeza que no me gusta incorporar el código fuente en el producto final, haciendo que esta enumeración sea muy superior. Añade que esta forma de enumeración es mucho más rápida. De hecho, esta forma de enumeración es el camino a seguir.

 10
Author: Jack Giffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-30 10:30:16

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
 9
Author: Xeltor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-15 04:20:02

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
 8
Author: David Miró,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-12 17:04:16

Use Javascript Proxies

Una característica muy beneficiosa de las Enumeraciones 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 enum burlada para evitar que se agreguen valores adicionales accidentalmente/maliciosamente, ninguna de las otras respuestas aborda esa característica intrínseca de Enum.

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

No me malinterprete, el comportamiento de JavaScript de devolver undefined cuando accede a propiedades indefinidas es en realidad una característica muy poderosa del lenguaje, pero no es una característica que desee cuando intenta burlarse de las estructuras de enumeración 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 usa para definir un comportamiento personalizado para operaciones fundamentales (por ejemplo, búsqueda de propiedades, asignación, enumeración, función invocación, etc).

Similar a un proxy de servidor web, los proxies JavaScript son capaces de interceptar operaciones en objetos (con el uso de "traps", llamarlos hooks si lo desea) y le permiten realizar varias comprobaciones, acciones y / o manipulaciones antes de que se completen (o en algunos casos detener las operaciones por completo que es exactamente lo que queremos hacer si y cuando tratamos de hacer referencia a un enumerador que no existe).

Aquí hay un ejemplo artificial que usa el objeto Proxy para imitar Enumeraciones. Los enumeradores en este ejemplo son métodos HTTP estándar (es decir, "GET", "POST", etc.):

// Generic Factory Function for creating enums (10 lines)
// Feel free to add this to your utility library in 
// your codebase and profit! Note: As Proxies are an ES6 
// feature, some browsers/clients may not support it and 
// you may need to transpile using a service like babel

function createEnum(enumObj) {

  // Instantiating a `Proxy` object requires two parameters, 
  // a `target` object and a `handler`. We first define the handler,
  // then use the handler to instantiate a Proxy.

  // a proxy handler is simply an object whose properties
  // are functions which define the behavior of the proxy 
  // when an operation is performed on it. For enums, we 
  // need to define behavior that lets us check what enumerator
  // is being accessed. This can be done by defining the "get" trap
  
  const enumHandler = {
    get: function(target, name) {
      if (target[name]) {
        return target[name]
      }
      throw new Error(`No such enumerator: ${name}`)
    }
  }
  
  
  // Freeze the target object to prevent modifications
  return new Proxy(Object.freeze(enumObj), enumHandler)
}


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

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

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

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

APARTE: ¿Qué diablos es un proxy?

Recuerdo cuando primero comenzó a ver la palabra proxy en todas partes, definitivamente no tenía sentido para mí durante mucho tiempo. Si ese eres tú en este momento, creo que una manera 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.

 7
Author: Govind Rai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-18 03:11:13

IE8 no soporta el método freeze ().
Fuente: http://kangax.github.io/compat-table/es5 / , Haga clic en " Mostrar navegadores obsoletos?"en la parte superior, y comprobar IE8 y congelar fila col intersección.

En mi proyecto de juego actual, he utilizado a continuación, ya que pocos clientes todavía utilizan 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 servidor y cliente intercambiando estos datos.
Por supuesto, ahora es su deber asegurarse de que no haya conflictos en los datos (RE, EX, etc. debe ser único, también 1, 2, etc. debe ser único). Tenga en cuenta que debe mantenerlos para siempre para la compatibilidad con versiones anteriores.

Asignación:

var wildType = CONST_WILD_TYPES.REGULAR;

Comparación:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}
 5
Author: Manohar Reddy Poreddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-02 04:50:50

He creado una clase Enum que puede obtener valores y nombres en O(1). También puede generar una Matriz de objetos que contenga 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 Enumeraciones en C#):

var val2 = enum1.item2;

Para obtener un nombre para un valor (puede ser ambiguo cuando se pone el mismo valor para nombres diferentes):

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 contiene:

"<option value='0'>item1</option>..."
 5
Author: Oooogi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-08 18:22:27

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"
 4
Author: user2254487,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-06 22:07:47

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

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í está un lodash mixin para crear una enumeración usando una cadena. Si bien esta versión es un poco más involucrada, hace la numeración automáticamente por usted. Todos los métodos lodash utilizados en este ejemplo tenga un equivalente regular de JavaScript, 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
 4
Author: Blake Bowen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-17 21:24:32

Acabo de publicar un paquete NPM gen_enum le permite crear rápidamente una estructura de datos enum en Javascript:

var genEnum = require('gen_enum');

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

Una cosa buena de esta pequeña herramienta es que 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

I obsolete gen_enum paquete y fusionar la función en constjs paquete, que proporciona más características incluyendo objetos inmutables, deserialización de cadenas JSON, constantes de cadenas y generación de mapas de bits, etc. Checkout https://www.npmjs.com/package/constjs para más información

Para actualizar de gen_enum a constjs simplemente cambie la instrucción

var genEnum = require('gen_enum');

A

var genEnum = require('constjs').enum;
 4
Author: Gelin Luo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-01 23:44:40

Se me ocurrió este enfoque que se modela a partir de enums en Java. Estos son seguros de tipo, por lo que también puede realizar comprobaciones instanceof.

Puedes definir enums como este:

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

Days ahora se refiere a la Days enum:

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 aplicació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
    }

})();
 4
Author: Vivin Paliath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-18 22:11:20

Aunque solo los métodos estáticos (y no las propiedades estáticas) son compatibles en ES2015 (ver aquí también, §15.2.2.2), curiosamente puede usar lo siguiente con Babel con el preajuste es2015:

class CellState {
    v: string;
    constructor(v: string) {
        this.v = v;
        Object.freeze(this);
    }
    static EMPTY       = new CellState('e');
    static OCCUPIED    = new CellState('o');
    static HIGHLIGHTED = new CellState('h');
    static values      = function(): Array<CellState> {
        const rv = [];
        rv.push(CellState.EMPTY);
        rv.push(CellState.OCCUPIED);
        rv.push(CellState.HIGHLIGHTED);
        return rv;
    }
}
Object.freeze(CellState);

Encontré que esto funciona como se esperaba incluso a través de módulos (por ejemplo, importando la enumeración CellState desde 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 static type checker (por ejemplo, Flow) y puede afirmar, en el tiempo de desarrollo utilizando static type checking, que sus variables, parámetros, etc. son de la específica CellState "enumeración" en lugar de alguna otra enumeración (que sería imposible de distinguir si se utilizan objetos genéricos o símbolos).

Actualización

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

  1. no se pueden crear más objetos de tipo CellState
  2. se le garantiza que no se asignen dos instancias de enumeración al mismo código
  3. método de utilidad para recuperar la enumeración de una representación de cadena
  4. La función values que devuelve todas las instancias de la enumeración no tiene que crear el valor devuelto en la anterior, manual (y propenso a errores) manera.

    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;
    
 4
Author: Marcus Junius Brutus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 22:38:00

Al momento de escribir, Octubre 2014 - así que aquí hay una solución contemporánea. Estoy escribiendo la solución como un Módulo de Nodo, y han incluido una prueba utilizando Mocha y Chai, así como underscoreJS. Puede ignorarlos fácilmente, y simplemente tomar el código Enum si lo prefiere.

He visto muchos posts con bibliotecas demasiado complicadas, etc. La solución para obtener soporte enum en Javascript es tan simple que realmente no es necesario. Aquí está el código:

Archivo: enumeraciones.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 puedes ver, obtienes una fábrica de Enum, puedes obtener todas las teclas simplemente llamando a enum.claves, y puede hacer coincidir las propias claves con constantes enteras. Y puede reutilizar la fábrica con diferentes valores y exportar esas Enum 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, potencialmente eliminando la biblioteca de guiones bajos también si no desea usarla en su código.

 3
Author: arcseldon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-20 17:44:48
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

No es necesario 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.

 3
Author: Shivanshu Goyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-29 01:32:23

Puedes hacer algo como esto

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

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

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


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

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

Como se define en esta biblioteca. https://github.com/webmodule/foo/blob/master/foo.js#L217

 3
Author: LNT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 06:32:25

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 clave

console.log(Object.keys(Status)[Status.Ready]) // Ready
 3
Author: Ilya Gazman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-17 12:38:45

Así es como Typescript traduce su 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 su simple - Operador de asignación asigna valor y luego lo devuelve:

obj['foo'] = 1
// => 1
 3
Author: Julius Dzidzevičius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-03 18:44:59

Lo había hecho hace un tiempo usando una mezcla de __defineGetter__ y __defineSetter__ o defineProperty dependiendo de la versión de JS.

Aquí está la función generadora de enum que hice: https://gist.github.com/gfarrell/6716853

Lo usarías así:

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

Y crearía una cadena inmutable:diccionario int (una enumeración).

 2
Author: GTF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-26 17:04:01

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

ACTUALIZACIÓN:

Ahí están mis códigos auxiliares(TypeHelper).

var Helper = {
    isEmpty: function (obj) {
        return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
    },

    isObject: function (obj) {
        return (typeof obj === 'object');
    },

    sortObjectKeys: function (object) {
        return Object.keys(object)
            .sort(function (a, b) {
                c = a - b;
                return c
            });
    },
    containsItem: function (arr, item) {
        if (arr && Array.isArray(arr)) {
            return arr.indexOf(item) > -1;
        } else {
            return arr === item;
        }
    },

    pushArray: function (arr1, arr2) {
        if (arr1 && arr2 && Array.isArray(arr1)) {
            arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
        }
    }
};
function TypeHelper() {
    var _types = arguments[0],
        _defTypeIndex = 0,
        _currentType,
        _value,
        _allKeys = Helper.sortObjectKeys(_types);

    if (arguments.length == 2) {
        _defTypeIndex = arguments[1];
    }

    Object.defineProperties(this, {
        Key: {
            get: function () {
                return _currentType;
            },
            set: function (val) {
                _currentType.setType(val, true);
            },
            enumerable: true
        },
        Value: {
            get: function () {
                return _types[_currentType];
            },
            set: function (val) {
                _value.setType(val, false);
            },
            enumerable: true
        }
    });
    this.getAsList = function (keys) {
        var list = [];
        _allKeys.forEach(function (key, idx, array) {
            if (key && _types[key]) {

                if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                    var json = {};
                    json.Key = key;
                    json.Value = _types[key];
                    Helper.pushArray(list, json);
                }
            }
        });
        return list;
    };

    this.setType = function (value, isKey) {
        if (!Helper.isEmpty(value)) {
            Object.keys(_types).forEach(function (key, idx, array) {
                if (Helper.isObject(value)) {
                    if (value && value.Key == key) {
                        _currentType = key;
                    }
                } else if (isKey) {
                    if (value && value.toString() == key.toString()) {
                        _currentType = key;
                    }
                } else if (value && value.toString() == _types[key]) {
                    _currentType = key;
                }
            });
        } else {
            this.setDefaultType();
        }
        return isKey ? _types[_currentType] : _currentType;
    };

    this.setTypeByIndex = function (index) {
        for (var i = 0; i < _allKeys.length; i++) {
            if (index === i) {
                _currentType = _allKeys[index];
                break;
            }
        }
    };

    this.setDefaultType = function () {
        this.setTypeByIndex(_defTypeIndex);
    };

    this.setDefaultType();
}

var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var enumA = new TypeHelper(TypeA, 4);

document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setType("200L", false);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

enumA.setDefaultType();
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setTypeByIndex(1);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

document.writeln("is equals = ", (enumA.Value == TypeA["2"]));
 2
Author: Sherali Turdiyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:32