una - typeof javascript example




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

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

my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
   // whatever
}

¿O hay un idioma más preferible?


Usa Javascript Proxies

TLDR: agregue esta clase a sus métodos de utilidad y utilícela en todo su código, simula el comportamiento de Enum desde los lenguajes de programación tradicionales y, en realidad, produce errores cuando intenta acceder a un enumerador que no existe o agregar / actualizar un enumerador. No hay necesidad de confiar en Object.freeze() .

class Enum {
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name];
        }
        throw new Error(`No such enumerator: ${name}`);
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    };

    return new Proxy(enumObj, handler);
  }
}

Luego crea enumeraciones al crear una instancia de la clase:

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

Explicación completa:

Una característica muy beneficiosa de los Enums que obtienes de los lenguajes tradicionales es que explotan (lanzan un error en tiempo de compilación) si intentas acceder a un enumerador que no existe.

Además de congelar la estructura de enumeración simulada para evitar que se agreguen accidentalmente / maliciosamente valores adicionales, ninguna de las otras respuestas aborda esa característica intrínseca de Enums.

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

No me malinterpretes, el comportamiento de JavaScript de volver undefined al acceder a propiedades no definidas es en realidad una característica muy poderosa del lenguaje, pero no es una característica que quieras cuando intentas burlarte de las estructuras de Enum tradicionales.

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

El objeto Proxy se utiliza para definir un comportamiento personalizado para operaciones fundamentales (por ejemplo, búsqueda de propiedades, asignación, enumeración, invocación de funciones, etc.).

De manera similar a un proxy de servidor web, los proxies de JavaScript pueden interceptar operaciones en objetos (con el uso de "trampas", llamarlos enganches si lo desea) y permitirle realizar varias verificaciones, acciones y / o manipulaciones antes de que se completen (o en algunos casos, detener las operaciones por completo, lo que es exactamente lo que queremos hacer cuando intentamos hacer referencia a un enumerador que no existe).

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

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

class Enum {
  // The Enum class instantiates a JavaScript Proxy object.
  // Instantiating a `Proxy` object requires two parameters, 
  // a `target` object and a `handler`. We first define the handler,
  // then use the handler to instantiate a Proxy.

  // A proxy handler is simply an object whose properties
  // are functions which define the behavior of the proxy 
  // when an operation is performed on it. 
  
  // For enums, we need to define behavior that lets us check what enumerator
  // is being accessed and what enumerator is being set. This can be done by 
  // defining "get" and "set" traps.
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name]
        }
        throw new Error(`No such enumerator: ${name}`)
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    }


    // Freeze the target object to prevent modifications
    return new Proxy(enumObj, handler)
  }
}


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

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

try {
  httpMethods.delete = "delete"
} catch (e) {
console.log("Error: ", e.message)
}
// throws "Cannot add/update properties on an Enum instance after it is defined"

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

ASIDE: ¿Qué diablos es un proxy?

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


Desde 1.8.5 es posible sellar y congelar el objeto, entonces defina lo anterior como:

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

o

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

¡y voilá! JS enums.

Nota: Lo escribí en 2011, pero es 2019: use const para evitar que se sobrescriba su diccionario de enumeración.

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

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

Una forma de garantizar un mayor grado de seguridad de tipos (con enumeraciones o de otro tipo) es usar una herramienta como TypeScript o Flow .

Source

Las citas no son necesarias, pero las mantuve por coherencia.


En pocas palabras: no se puede.

Puedes fingirlo, pero no obtendrás el tipo de seguridad. Normalmente, esto se hace creando un diccionario simple de valores de cadena asignados a valores enteros. Por ejemplo:

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}

Document.Write("Enumerant: " + DaysEnum.tuesday);

¿El problema con este enfoque? Puede redefinir accidentalmente su enumerante, o accidentalmente tener valores duplicados duplicados. Por ejemplo:

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

Editar

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

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

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

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

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

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


Esta es la solución que uso.

function Enum() {
    this._enums = [];
    this._lookups = {};
}

Enum.prototype.getEnums = function() {
    return _enums;
}

Enum.prototype.forEach = function(callback){
    var length = this._enums.length;
    for (var i = 0; i < length; ++i){
        callback(this._enums[i]);
    }
}

Enum.prototype.addEnum = function(e) {
    this._enums.push(e);
}

Enum.prototype.getByName = function(name) {
    return this[name];
}

Enum.prototype.getByValue = function(field, value) {
    var lookup = this._lookups[field];
    if(lookup) {
        return lookup[value];
    } else {
        this._lookups[field] = ( lookup = {});
        var k = this._enums.length - 1;
        for(; k >= 0; --k) {
            var m = this._enums[k];
            var j = m[field];
            lookup[j] = m;
            if(j == value) {
                return m;
            }
        }
    }
    return null;
}

function defineEnum(definition) {
    var k;
    var e = new Enum();
    for(k in definition) {
        var j = definition[k];
        e[k] = j;
        e.addEnum(j)
    }
    return e;
}

Y definas tus enums de esta manera:

var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

Y así es como accedes a tus enumeraciones:

COLORS.BLUE.string
COLORS.BLUE.value
COLORS.getByName('BLUE').string
COLORS.getByValue('value', 1).string

COLORS.forEach(function(e){
    // do what you want with e
});

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

Algunas ventajas de este enfoque:

  • Fácil de declarar enumeraciones
  • Fácil acceso a sus enums
  • Sus enums pueden ser tipos complejos
  • La clase Enum tiene cierto almacenamiento en caché asociativo si está utilizando mucho getByValue

Algunas desventajas:

  • Hay un poco de gestión de memoria desordenada allí, ya que mantengo las referencias a las enumeraciones
  • Todavía no hay seguridad tipo

Esto es lo que todos queremos:

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

Ahora puedes crear tus enums:

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

Al hacer esto, se puede acceder a las constantes de la forma habitual (SíNo.SÍ, Color.GREEN) y obtienen un valor int secuencial (NO = 0, SÍ = 1; ROJO = 0, VERDE = 1, AZUL = 2).

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

Enum.prototype.values = function() {
    return this.allValues;
    /* for the above to work, you'd need to do
            this.allValues = constantsList at the constructor */
};


Edición - pequeña mejora - ahora con varargs: (desafortunadamente no funciona correctamente en IE: S ... debería seguir con la versión anterior)

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

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

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

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


La "Sintaxis preferida" por la mayoría de las personas ya se ha enumerado anteriormente. Sin embargo, hay un problema principal: el tamaño del código. Cada otra respuesta que aparece aquí aumenta el tamaño de su código hasta el extremo. Para obtener el mejor rendimiento posible, la legibilidad del código, la gestión de proyectos a gran escala, las sugerencias de sintaxis en muchos editores de códigos y la reducción del tamaño del código por reducción, esta es la forma correcta de hacer las enumeraciones.

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

// later on

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

Además, esta sintaxis permite una clase clara y concisa que se extiende como se ve a continuación.

(Longitud: 2,450 bytes)

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

    const ENUM_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          ENUMLEN_PIXELCOLOR   = 1,
          ENUM_SOLIDCOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_SOLIDCOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_SOLIDCOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUMLEN_SOLIDCOLOR   = ENUMLEN_PIXELCOLOR+3,
          ENUM_ALPHACOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_ALPHACOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_ALPHACOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUM_ALPHACOLOR_A    = ENUMLEN_PIXELCOLOR+3,
          ENUMLEN_ALPHACOLOR   = ENUMLEN_PIXELCOLOR+4,
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

Algunos pueden decir que esto es menos práctico que otras soluciones: elimina toneladas de espacio, lleva mucho tiempo escribir y no está recubierto con sintaxis de azúcar. Esas personas tendrían razón si no minimizan su código. Sin embargo, ninguna persona razonable dejaría un código no minado en el producto final. Para esta minificación, Closure Compiler es lo mejor que todavía tengo que encontrar. El acceso en línea se puede encontrar here . El compilador de cierre es capaz de tomar todos estos datos de enumeración e integrarlos, haciendo que tu Javascript sea super duper pequeño y se ejecute super duper rápidamente. Observar.

(Longitud: 605 bytes)

'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);

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

Fuente sin utilizar enumeraciones (longitud: 1,973 bytes (477 bytes más cortos que el código enumerado))
Reducido sin usar enumeraciones (longitud: 843 bytes (238 bytes más que el código enumerado ))

Como se ve, sin enumeración, el código fuente es más corto al costo de un código minificado más grande. No sé sobre ti; pero estoy seguro de que no incorporo el código fuente en el producto final. Por lo tanto, esta forma de enumeración es muy superior en la medida en que da como resultado tamaños de archivo minificados más pequeños.

Otra ventaja de esta forma de enumeración es que se puede usar para administrar fácilmente proyectos a gran escala sin sacrificar el tamaño del código reducido. Cuando se trabaja en un proyecto grande con muchas otras personas, podría ser beneficioso marcar y etiquetar explícitamente los nombres de las variables con quienes crearon el código para que el creador original del código pueda ser identificado rápidamente para corregir errores en colaboración.

// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED   = 0,
      ENUM_JG_COLORENUM_GREEN = 1,
      ENUM_JG_COLORENUM_BLUE  = 2,
      ENUMLEN_JG_COLORENUM    = 3;

// later on

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

// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED   = 0,
      ENUM_PL_ARRAYTYPE_ISSORTED   = 1,
      ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob Knight
      ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
      ENUMLEN_PL_COLORENUM         = 4;

// later on

if(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

Además, esta forma de enumeración también es mucho más rápida después de la reducción. En las propiedades con nombre normales, el navegador tiene que usar mapas de hash para buscar dónde está la propiedad en el objeto. Aunque los compiladores JIST almacenan de manera inteligente esta ubicación en el objeto, todavía hay una sobrecarga tremenda para casos especiales, como eliminar una propiedad inferior del objeto. Pero, con matrices PACKED_ELEMENTS índice entero no dispersas continuas, el navegador puede omitir gran parte de esa sobrecarga porque el índice del valor en la matriz interna ya está especificado. Sí, de acuerdo con el estándar ECMAScript, todas las propiedades deben tratarse como cadenas. Sin embargo, este aspecto del estándar ECMAScript es muy engañoso sobre el rendimiento porque todos los navegadores tienen optimizaciones especiales para índices numéricos en matrices.

Además, mi cereza personal en la parte superior está utilizando esta forma de enumeración junto con el editor de texto CodeMirror en modo Javascript. El modo de resaltado de sintaxis de Javascript de CodeMirror resalta las variables locales en el alcance actual. De esa manera, sabrá al instante cuando escribe un nombre de variable correctamente porque si el nombre de la variable se declaró previamente con la palabra clave var , entonces el nombre de la variable cambia a un color especial (cian por defecto). Incluso si no usa CodeMirror, al menos el navegador arroja una útil [variable name] is not defined excepción [variable name] is not defined al ejecutar código con nombres de enumeración mal escritos. Además, las herramientas de JavaScript como JSLint y Closure Compiler son muy fuertes al decirle cuando escribe mal un nombre de variable de enumeración. CodeMirror, el navegador y varias herramientas de Javascript juntas hacen que la depuración de esta forma de enumeración sea muy simple y realmente fácil.

Por lo tanto, llego a la conclusión de que, de hecho, esta forma de enumeración es la forma de avanzar no solo para el tamaño del código reducido, sino también para el rendimiento, la claridad y la colaboración.


ACTUALIZACIÓN : Gracias por todos los upvotes a todos, pero no creo que mi respuesta a continuación sea la mejor manera de escribir enumeraciones en Javascript. Ver mi blog para más detalles: Enums en Javascript .

Alertar el nombre ya es posible:

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

Alternativamente, puedes hacer que los valores sean objetos, para que puedas tener la torta y comerla también:

var SIZE = {
  SMALL : {value: 0, name: "Small", code: "S"}, 
  MEDIUM: {value: 1, name: "Medium", code: "M"}, 
  LARGE : {value: 2, name: "Large", code: "L"}
};

var currentSize = SIZE.MEDIUM;
if (currentSize == SIZE.MEDIUM) {
  // this alerts: "1: Medium"
  alert(currentSize.value + ": " + currentSize.name);
}

En Javascript, como es un lenguaje dinámico, incluso es posible agregar valores de enumeración al conjunto más adelante:

// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};

Recuerde, los campos de la enumeración (valor, nombre y código en este ejemplo) no son necesarios para la verificación de identidad y solo están allí para su comodidad. Además, el nombre de la propiedad de tamaño en sí no necesita estar codificado, sino que también puede establecerse dinámicamente. Entonces, suponiendo que solo conoce el nombre de su nuevo valor de enumeración, aún puede agregarlo sin problemas:

// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};

Por supuesto, esto significa que ya no se pueden hacer algunas suposiciones (ese valor representa el orden correcto para el tamaño, por ejemplo).

Recuerde, en Javascript un objeto es como un mapa o una tabla hash. Un conjunto de pares nombre-valor. Puede recorrerlos o manipularlos sin saber mucho de antemano.

P.EJ:

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

Y por cierto, si está interesado en los espacios de nombres, es posible que desee echar un vistazo a mi solución para el simple pero potente espacio de nombres y la gestión de dependencias para javascript: Paquetes JS


Crear un objeto literal:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};

He modificado la solución de Andre 'Fi':

  function Enum() {
    var that = this;
    for (var i in arguments) {
        that[arguments[i]] = i;
    }
    this.name = function(value) {
        for (var key in that) {
            if (that[key] == value) {
                return key;
            }
        }
    };
    this.exist = function(value) {
        return (typeof that.name(value) !== "undefined");
    };
    if (Object.freeze) {
        Object.freeze(that);
    }
  }

Prueba:

var Color = new Enum('RED', 'GREEN', 'BLUE');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true

Una forma rápida y sencilla sería:

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

console.log(Colors.WHITE)  //this prints out "0"

tus respuestas son demasiado complicadas

var buildSet = function(array) {
  var set = {};
  for (var i in array) {
    var item = array[i];
    set[item] = item;
  }
  return set;
}

var myEnum = buildSet(['RED','GREEN','BLUE']);
// myEnum.RED == 'RED' ...etc

Acabo de publicar un paquete NPM gen_enum te permite crear la estructura de datos Enum en Javascript rápidamente:

var genEnum = require('gen_enum');

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

Una cosa buena de esta pequeña herramienta es en el entorno moderno (incluidos los navegadores nodejs e IE 9+), el objeto Enum devuelto es inmutable.

Para obtener más información, visite https://github.com/greenlaw110/enumjs

Actualizaciones

constjs obsoleto el gen_enumpaquete y constjs la función en el paquete constjs , que proporciona más funciones que incluyen objetos inmutables, deserialización de cadenas JSON, constantes de cadenas y generación de mapas de bits, etc. Visite constjs para obtener más información

Para actualizar desde gen_enumque constjsacaba de cambiar la declaración

var genEnum = require('gen_enum');

a

var genEnum = require('constjs').enum;

Aquí hay un par de maneras diferentes para implementar las enumeraciones de TypeScript .

La forma más sencilla es simplemente iterar sobre un objeto, agregando pares de clave-valor invertidos al objeto. El único inconveniente es que debe establecer manualmente el valor para cada miembro.

function _enum(list) {       
  for (var key in list) {
    list[list[key] = list[key]] = key;
  }
  return Object.freeze(list);
}

var Color = _enum({
  Red: 0,
  Green: 5,
  Blue: 2
});

// Color → {0: "Red", 2: "Blue", 5: "Green", "Red": 0, "Green": 5, "Blue": 2}
// Color.Red → 0
// Color.Green → 5
// Color.Blue → 2
// Color[5] → Green
// Color.Blue > Color.Green → false


Y aquí hay un mixin de lodash para crear una enumeración usando una cadena. Si bien esta versión es un poco más complicada, realiza la numeración automáticamente para usted. Todos los métodos de lodash utilizados en este ejemplo tienen un equivalente de JavaScript regular, por lo que puede cambiarlos fácilmente si lo desea.

function enum() {
    var key, val = -1, list = {};
    _.reduce(_.toArray(arguments), function(result, kvp) {    
        kvp = kvp.split("=");
        key = _.trim(kvp[0]);
        val = _.parseInt(kvp[1]) || ++val;            
        result[result[val] = key] = val;
        return result;
    }, list);
    return Object.freeze(list);
}    

// Add enum to lodash 
_.mixin({ "enum": enum });

var Color = _.enum(
    "Red",
    "Green",
    "Blue = 5",
    "Yellow",
    "Purple = 20",
    "Gray"
);

// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue

Aunque solo los métodos estáticos (y no las propiedades estáticas) son compatibles con ES2015 (ver here también, §15.2.2.2), curiosamente, puede usar los siguientes con Babel con el es2015preajuste:

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

Descubrí que esto funciona como se esperaba, incluso a través de los módulos (por ejemplo, la importación de la CellStateenumeración de otro módulo) y también cuando importo un módulo usando Webpack.

La ventaja que este método tiene sobre la mayoría de las otras respuestas es que puede usarlo junto con un verificador de tipo estático (por ejemplo, Flow ) y puede afirmar, en el momento del desarrollo utilizando la verificación de tipo estática, que sus variables, parámetros, etc. son específicos CellState" enumeración "en lugar de alguna otra enumeración (que sería imposible distinguir si usara símbolos o objetos genéricos).

actualizar

El código anterior tiene una deficiencia en el sentido de que permite crear objetos adicionales de tipo CellState(aunque no se pueden asignar a los campos estáticos CellStatedesde que se congela). Aún así, el código más refinado a continuación ofrece las siguientes ventajas:

  1. no CellStatese pueden crear más objetos de tipo
  2. se le garantiza que no hay dos instancias de enumeración 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;

En ES7 , puede hacer un ENUM elegante basándose en atributos estáticos:

class ColorEnum  {
    static RED = 0 ;
    static GREEN = 1;
    static BLUE = 2;
}

entonces

if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}

La ventaja (de la utilización de clase en lugar de objeto literal) es tener una clase padre Enumentonces todas sus enumeraciones se extiende esa clase.

 class ColorEnum  extends Enum {/*....*/}

Escribí enumeración en una biblioteca muy pequeña para abordar el problema que garantiza la seguridad de los tipos , permitir que las constantes de enumeración hereden de un prototipo , las constantes de enumeración de las garantías y los tipos de enumeración sean inmutables + muchas características pequeñas. Permite refactorizar una gran cantidad de código y mover algo de lógica dentro de la definición de enumeración. Aquí hay un ejemplo :

var CloseEventCodes = new Enumeration("closeEventCodes", {
  CLOSE_NORMAL:          { _id: 1000, info: "Connection closed normally" },
  CLOSE_GOING_AWAY:      { _id: 1001, info: "Connection closed going away" },
  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: "Connection closed due to protocol error"  },
  CLOSE_UNSUPPORTED:     { _id: 1003, info: "Connection closed due to unsupported operation" },
  CLOSE_NO_STATUS:       { _id: 1005, info: "Connection closed with no status" },
  CLOSE_ABNORMAL:        { _id: 1006, info: "Connection closed abnormally" },
  CLOSE_TOO_LARGE:       { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
    console.log(this.info); 
  }
});


CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true

Enumeration Es básicamente una fábrica.

Guía completamente documentada disponible aquí. Espero que esto ayude.


He creado una clase Enum que puede recuperar valores Y nombres en O (1). También puede generar una matriz de objetos que contiene todos los nombres y valores.

function Enum(obj) {
    // Names must be unique, Values do not.
    // Putting same values for different Names is risky for this implementation

    this._reserved = {
        _namesObj: {},
        _objArr: [],
        _namesArr: [],
        _valuesArr: [],
        _selectOptionsHTML: ""
    };

    for (k in obj) {
        if (obj.hasOwnProperty(k)) {
            this[k] = obj[k];
            this._reserved._namesObj[obj[k]] = k;
        }
    }
}
(function () {
    this.GetName = function (val) {
        if (typeof this._reserved._namesObj[val] === "undefined")
            return null;
        return this._reserved._namesObj[val];
    };

    this.GetValue = function (name) {
        if (typeof this[name] === "undefined")
            return null;
        return this[name];
    };

    this.GetObjArr = function () {
        if (this._reserved._objArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push({
                            Name: k,
                            Value: this[k]
                        });
            }
            this._reserved._objArr = arr;
        }
        return this._reserved._objArr;
    };

    this.GetNamesArr = function () {
        if (this._reserved._namesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(k);
            }
            this._reserved._namesArr = arr;
        }
        return this._reserved._namesArr;
    };

    this.GetValuesArr = function () {
        if (this._reserved._valuesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(this[k]);
            }
            this._reserved._valuesArr = arr;
        }
        return this._reserved._valuesArr;
    };

    this.GetSelectOptionsHTML = function () {
        if (this._reserved._selectOptionsHTML.length == 0) {
            var html = "";
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        html += "<option value='" + this[k] + "'>" + k + "</option>";
            }
            this._reserved._selectOptionsHTML = html;
        }
        return this._reserved._selectOptionsHTML;
    };
}).call(Enum.prototype);

Puedes iniciarlo así:

var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

Para obtener un valor (como Enums en C #):

var val2 = enum1.item2;

Para obtener un nombre para un valor (puede ser ambiguo al poner el mismo valor para diferentes nombres):

var name1 = enum1.GetName(0);  // "item1"

Para obtener una matriz con cada nombre y valor en un objeto:

var arr = enum1.GetObjArr();

Generará:

[{ Name: "item1", Value: 0}, { ... }, ... ]

También puede obtener las opciones de selección de html fácilmente:

var html = enum1.GetSelectOptionsHTML();

Que sostiene:

"<option value='0'>item1</option>..."

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

Puedes 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
    }

})();

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

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

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

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

// using
Colors.Red.print()






enums