función cifrada
Estoy reconstruyendo un antiguo proyecto Java en Javascript y me di cuenta de que no hay una buena manera de hacer enumeraciones en JS.
Lo mejor que se me ocurre es:
const Colors = {
RED: Symbol("red"),
BLUE: Symbol("blue"),
GREEN: Symbol("green")
};
Object.freeze(Colors);
los const
mantiene Colors
ser reasignado, y congelarlo evita la mutación de claves y valores. Estoy usando símbolos para que Colors.RED
no es igual a 0
o cualquier otra cosa además de sí mismo.
¿Hay algún problema con esta formulación? ¿Hay una mejor manera?
(Sé que esta pregunta se repite un poco, pero todas las preguntas y respuestas anteriores son bastante antiguas y ES6 nos brinda algunas capacidades nuevas).
EDITAR:
Otra solución, que se ocupa del problema de serialización, pero creo que todavía tiene problemas de reino:
const enumValue = (name) => Object.freeze({toString: () => name});
const Colors = Object.freeze({
RED: enumValue("Colors.RED"),
BLUE: enumValue("Colors.BLUE"),
GREEN: enumValue("Colors.GREEN")
});
Al utilizar referencias a objetos como valores, obtiene la misma prevención de colisiones que los símbolos.
Bergi
¿Hay algún problema con esta formulación?
no veo ninguno
¿Hay una mejor manera?
Colapsaría las dos declaraciones en una sola:
const Colors = Object.freeze({
RED: Symbol("red"),
BLUE: Symbol("blue"),
GREEN: Symbol("green")
});
Si no te gusta el repetitivo, dale me gusta al repetido Symbol
llamadas, por supuesto también puede escribir una función de ayuda makeEnum
que crea lo mismo a partir de una lista de nombres.
-
¿No hay problemas de reino aquí?
– usuario663031
9 de junio de 2017 a las 3:28
-
@torazaburo ¿Quiere decir que cuando el código se carga dos veces generará diferentes símbolos, lo que no sería un problema con las cadenas? Sí, buen punto, que sea una respuesta 🙂
– Bergi
9 de junio de 2017 a las 3:44
-
@ErictheRed No,
Symbol.for
lo hace no tiene problemas entre reinos, sin embargo, tiene el problema habitual de colisión con un espacio de nombres verdaderamente global.– Bergi
13 de junio de 2017 a las 0:11
-
@ErictheRed De hecho, garantiza crear exactamente el mismo símbolo independientemente de cuándo y dónde (desde qué reino/marco/pestaña/proceso) se llama
– Bergi
23 de junio de 2017 a las 5:34
-
@Sky Un valor predeterminado para la búsqueda en
Colors
no tiene nada que ver con la definición de enumeración. Uno haría eso como de costumbre, conColors[name] || Colors.BLUE
oColors.hasOwnProperty(name) ? Colors[name] : Colors.BLUE
.– Bergi
12 de febrero de 2019 a las 17:02
justin esmeril
mientras usa Symbol
como el valor de enumeración funciona bien para casos de uso simples, puede ser útil dar propiedades a las enumeraciones. Esto se puede hacer usando un Object
como el valor de enumeración que contiene las propiedades.
Por ejemplo, podemos dar a cada uno de los Colors
un nombre y un valor hexadecimal:
/**
* Enum for common colors.
* @readonly
* @enum {{name: string, hex: string}}
*/
const Colors = Object.freeze({
RED: { name: "red", hex: "#f00" },
BLUE: { name: "blue", hex: "#00f" },
GREEN: { name: "green", hex: "#0f0" }
});
Incluir propiedades en la enumeración evita tener que escribir switch
declaraciones (y posiblemente olvidar nuevos casos a las declaraciones de cambio cuando se extiende una enumeración). El ejemplo también muestra las propiedades de enumeración y los tipos documentados con el Anotación de enumeración JSDoc.
La igualdad funciona como se esperaba con Colors.RED === Colors.RED
siendo true
y Colors.RED === Colors.BLUE
siendo false
.
-
Tenga en cuenta que Object.freeze no es profundo, por ejemplo, podría cambiar Colors.RED.name = “charlie”; Podría usar algo como esto para congelar objetos anidados: const deepFreeze = obj => { Object.keys(obj).forEach(prop => { if (typeof obj[prop] === ‘objeto’) deepFreeze(obj[prop]); }); devuelve Object.freeze(obj); };
– punta de piedra
5 de julio a las 19:37
Este es mi enfoque personal.
class ColorType {
static get RED () {
return "red";
}
static get GREEN () {
return "green";
}
static get BLUE () {
return "blue";
}
}
// Use case.
const color = Color.create(ColorType.RED);
-
No recomendaría usar esto ya que no proporciona una forma de iterar sobre todos los valores posibles, y no hay forma de verificar si un valor es un ColorType sin verificar manualmente cada uno.
– Dominó
6 de abril de 2020 a las 7:10
-
Me temo que es demasiado código para definir un tipo Enum, que debería ser muy conciso
– Zhe
5 de enero a las 15:16
Como se mencionó anteriormente, también puede escribir un makeEnum()
función auxiliar:
function makeEnum(arr){
let obj = {};
for (let val of arr){
obj[val] = Symbol(val);
}
return Object.freeze(obj);
}
Úsalo así:
const Colors = makeEnum(["red","green","blue"]);
let startColor = Colors.red;
console.log(startColor); // Symbol(red)
if(startColor == Colors.red){
console.log("Do red things");
}else{
console.log("Do non-red things");
}
si no necesitas puro ES6 y puede usar Typescript, tiene un buen enum
:
Actualización 11.05.2020:
Modificado para incluir campos y métodos estáticos para replicar más de cerca el comportamiento de enumeración “verdadero”.
Si planea actualizar, le recomendaría intentar usar lo que llamo una “Clase Enum” (salvo cualquier limitación del navegador o entorno de tiempo de ejecución que no pueda aceptar). es básicamente un muy simple y una clase limpia que usa campos privados y accesos limitados para simular el comportamiento de una enumeración. Esto es algo que a veces hago en C# cuando quiero incorporar más funciones en una enumeración.
Me doy cuenta de que los campos de clase privada aún son experimentales en este momento, pero parece funcionar para crear una clase con campos/propiedades inmutables. El soporte del navegador también es decente. Los únicos navegadores “principales” que no lo admiten son Firefox (que estoy seguro de que lo harán pronto) e IE (a quién le importa).
DESCARGO DE RESPONSABILIDAD:
No soy un desarrollador. Acabo de armar esto para resolver las limitaciones de las enumeraciones inexistentes en JS cuando estaba trabajando en un proyecto personal.
Clase de muestra
class Colors {
// Private Fields
static #_RED = 0;
static #_GREEN = 1;
static #_BLUE = 2;
// Accessors for "get" functions only (no "set" functions)
static get RED() { return this.#_RED; }
static get GREEN() { return this.#_GREEN; }
static get BLUE() { return this.#_BLUE; }
}
Ahora debería poder llamar a sus enumeraciones directamente.
Colors.RED; // 0
Colors.GREEN; // 1
Colors.BLUE; // 2
La combinación de usar campos privados y accesos limitados significa que los valores de enumeración existentes están bien protegidos (ahora son esencialmente constantes).
Colors.RED = 10 // Colors.RED is still 0
Colors._RED = 10 // Colors.RED is still 0
Colors.#_RED = 10 // Colors.RED is still 0
Controlar cómo lo hace TypeScript. Básicamente hacen lo siguiente:
const MAP = {};
MAP[MAP[1] = 'A'] = 1;
MAP[MAP[2] = 'B'] = 2;
MAP['A'] // 1
MAP[1] // A
Usa símbolos, congela objetos, lo que quieras.
-
No estoy siguiendo por qué usa
MAP[MAP[1] = 'A'] = 1;
en vez deMAP[1] = 'A'; MAP['A'] = 1;
. Siempre he oído que usar una tarea como expresión es de mal estilo. Además, ¿qué beneficio obtiene de las asignaciones duplicadas?– función de cifrado
27 de abril de 2018 a las 17:05
-
Aquí hay un enlace a cómo se compila el mapeo de enumeración en es5 en sus documentos. typescriptlang.org/docs/handbook/enums.html#reverse-mappings Puedo imaginar que simplemente sería más fácil y más conciso compilarlo en una sola línea, por ejemplo
MAP[MAP[1] = 'A'] = 1;
.– dar abrazo
2 de mayo de 2018 a las 9:21
-
Eh. Por lo tanto, parece que la duplicación solo facilita el cambio entre las representaciones de cadena y número/símbolo de cada valor, y verifica que alguna cadena o número/símbolo
x
es un valor Enum válido haciendoEnum[Enum[x]] === x
. No resuelve ninguno de mis problemas originales, pero podría ser útil y no rompe nada.– función de cifrado
2 mayo 2018 a las 16:13
-
Tenga en cuenta que TypeScript agrega una capa de robustez que se pierde una vez que se compila el código TS. Si toda su aplicación está escrita en TS, es excelente, pero si desea que el código JS sea sólido, el mapa congelado de símbolos suena como un patrón más seguro.
– Dominó
6 de abril de 2020 a las 7:08
este sería un enfoque perfecto en es6. No tienes que congelarlo
– NiRUS
9 de junio de 2017 a las 1:35
@Nirus lo haces, si no quieres que se modifique.
– zerkms
9 de junio de 2017 a las 1:37
¿Te diste cuenta de esta respuesta?
– Bergi
9 de junio de 2017 a las 1:47
Un problema que se me ocurre: no se puede usar esta enumeración con
JSON.stringify()
. No se puede serializar / deserializarSymbol
.– le_m
9 de junio de 2017 a las 12:27
@ErictheRed He estado usando valores constantes de enumeración de cadenas durante años sin problemas, porque el uso de Flow (o TypeScript) garantiza mucha más seguridad de tipos que preocuparse por evitar colisiones.
– Andy
19 de diciembre de 2018 a las 2:21