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


1675
2017-11-13 19:09


origen


Respuestas:


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

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

o

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

¡y voilá! JS enums.

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 tipo (con enumeraciones u otras) es usar una herramienta como Mecanografiado o Fluir.

Fuente

Las cotizaciones no son necesarias, pero las conservé para mantener la coherencia.


539
2018-02-18 11:03



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

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


583
2017-11-13 19:13



ACTUALIZAR: Gracias por todos los upvotes, pero no creo que mi respuesta a continuación sea la mejor forma de escribir enumeraciones en Javascript nunca más. Ver la publicación de mi blog para más detalles: Enumerados en Javascript.


Alertar el nombre ya es posible:

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

Alternativamente, puedes hacer los objetos de valores para que puedas tener el pastel y comértelo 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 enum 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 la verificación de identidad y solo están disponibles para su comodidad. Además, el nombre de la propiedad del tamaño en sí mismo no necesita estar codificado, sino que también se puede configurar dinámicamente. Entonces, suponiendo que solo conozca el nombre de su nuevo valor enum, 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 pueden hacerse (ese valor representa el orden correcto para el tamaño, por ejemplo).

Recuerde, en Javascript un objeto es como un mapa o hashtable. Un conjunto de pares nombre-valor. Puede recorrerlos o manipularlos sin saber mucho antes de ellos.

P.EJ:

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

Y, por cierto, si le interesan los espacios de nombres, le recomendamos echarle un vistazo a mi solución de administración de dependencias y espacio de nombres simple pero potente para javascript: Paquetes JS


477
2018-03-04 22:31



En pocas palabras: no puedes.

Puedes fingirlo, pero no obtendrás seguridad de tipo. Por lo general, esto se hace creando un diccionario simple de valores de cadena mapeados 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 enumerador o accidentalmente tener valores de enumerador duplicados. Por ejemplo:

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

Editar 

¿Qué hay de Object.freeze de Artur Czajka? ¿No funcionaría eso para evitar que establezcas de lunes a jueves? - Fry Quad

Absolutamente, Object.freeze arreglaría totalmente el problema del que me quejé. Me gustaría recordarles a todos que cuando escribí lo anterior, Object.freeze realmente no existió

Ahora ... ahora abre algunas muy posibilidades interesantes

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

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

Si bien es probable que no se ajuste a todos los usos válidos de enumeraciones, va un largo camino.


79
2017-08-21 20:56



Esto es lo que todos queremos:

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

Ahora puedes crear tus enums:

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

Al hacer esto, las constantes se pueden procesar de la manera habitual (YesNo.YES, Color.GREEN) y obtienen un valor int secuencial (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 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 ... 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');

46
2017-07-13 00:28



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 de tipo de la enumeración ya que JavaScript garantiza que cada valor de símbolo es ú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 enum:

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

Demo de Plunker

En GitHub puedes 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

41
2018-05-05 16:32



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

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

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

Usando este método ... debería (en teoría) poder llamar y definir valores enum 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 esta debería hacer que escriba seguro.

Entonces debería poder crear un objeto personalizado, luego llamar Enum() en eso. Los valores asignados comienzan en 0 e incrementan por artículo.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

23
2017-08-21 10:34



Este es un viejo que sé, 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 en ambos MyEnum.Bar que devuelve 1, y MyEnum[1] que devuelve "Barra" independientemente del orden de la declaración.


17
2018-06-24 16:11



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

Usualmente uso los últimos dos métodos para mapear enumeraciones de objetos de mensaje.

Algunas ventajas de este enfoque:

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

Algunas desventajas:

  • Algo de administración de memoria desordenada sucede allí, ya que guardo las referencias a las enumeraciones
  • Todavía no hay seguridad de tipo

15
2018-05-15 09:26