Quelle est la syntaxe préférée pour définir les énumérations en JavaScript? Quelque chose comme:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
Ou existe-t-il un idiome plus préférable?
Depuis la 1.8.5, il est possible de sceller et de geler l'objet, définissez donc ce qui précède comme suit:
var DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})
ou
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)
et le tour est joué! JS enums.
Toutefois, cela ne vous empêche pas d'attribuer une valeur non souhaitée à une variable, ce qui est souvent l'objectif principal des énumérations:
let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors
Un moyen de renforcer le degré de sécurité des types (avec enums ou autrement) consiste à utiliser un outil tel que TypeScript ou Flow .
Les citations ne sont pas nécessaires mais je les ai conservées pour la cohérence.
Ce n'est pas vraiment une réponse, mais je dirais que ça marche très bien, personnellement
Cela dit, étant donné que peu importe la valeur des valeurs (vous avez utilisé 0, 1, 2), j'utiliserais une chaîne explicite au cas où vous voudriez jamais afficher la valeur actuelle.
UPDATE: Merci à tous pour vos votes, mais je ne pense pas que ma réponse ci-dessous soit la meilleure façon d'écrire des énumérations en Javascript. Voir mon article de blog pour plus de détails: Enums in Javascript .
Alerter le nom est déjà possible:
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);
}
}
Vous pouvez également créer des objets de valeur pour avoir le gâteau et le manger aussi:
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, comme il s’agit d’un langage dynamique, il est même possible d’ajouter ultérieurement des valeurs énum à l’ensemble:
// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};
N'oubliez pas que les champs de l'énumération (valeur, nom et code dans cet exemple) ne sont pas nécessaires pour le contrôle d'identité et ne sont là que pour votre commodité. De plus, le nom de la propriété de taille elle-même n'a pas besoin d'être codé en dur, mais peut également être défini de manière dynamique. Donc, si vous ne connaissez que le nom de votre nouvelle valeur enum, vous pouvez toujours l’ajouter sans problèmes:
// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};
Bien entendu, cela signifie que certaines hypothèses ne peuvent plus être émises (cette valeur représente le bon ordre pour la taille, par exemple).
N'oubliez pas qu'en Javascript un objet ressemble à une carte ou à une table de hachage. Un ensemble de paires nom-valeur. Vous pouvez les parcourir ou les manipuler d’une autre manière sans trop en savoir plus à leur sujet.
PAR EXEMPLE:
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.
}
}
En outre, si les espaces de noms vous intéressent, vous pouvez consulter ma solution pour la gestion simple mais puissante des espaces de noms et de la dépendance pour javascript: Packages JS
Conclusion: vous ne pouvez pas.
Vous pouvez faire semblant, mais vous ne serez pas en sécurité. Cela se fait généralement en créant un dictionnaire simple de valeurs de chaîne mappées sur des valeurs entières. Par exemple:
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Document.Write("Enumerant: " + DaysEnum.tuesday);
Le problème avec cette approche? Vous pouvez redéfinir accidentellement votre énumérateur ou avoir des valeurs énumératives en double. Par exemple:
DaysEnum.monday = 4; // whoops, monday is now thursday, too
Modifier
Qu'en est-il de Object.freeze d'Artur Czajka? Cela ne vous empêcherait-il pas de vous coucher du lundi au jeudi? - Fry Quad
Absolument, Object.freeze
réglerait totalement le problème dont je me plains. Je voudrais rappeler à tous que lorsque j'ai écrit ce qui précède, Object.freeze
n'existait pas vraiment.
Maintenant ... maintenant cela ouvre quelques très possibilités intéressantes.
Edit 2
Voici une très bonne bibliothèque pour créer des énumérations.
http://www.2ality.com/2011/10/enums.html
Bien que cela ne corresponde probablement pas à toutes les utilisations valables des énumérations, cela va très loin.
Voici ce que nous voulons tous:
function Enum(constantsList) {
for (var i in constantsList) {
this[constantsList[i]] = i;
}
}
Maintenant, vous pouvez créer vos enums:
var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);
Ainsi, les constantes peuvent être obtenues de la manière habituelle (YesNo.YES, Color.GREEN) et elles obtiennent une valeur int séquentielle (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).
Vous pouvez également ajouter des méthodes, en utilisant 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 */
};
Edit - petite amélioration - maintenant avec varargs: (malheureusement, cela ne fonctionne pas correctement sous IE: S ... devrait rester avec la version précédente alors)
function Enum() {
for (var i in arguments) {
this[arguments[i]] = i;
}
}
var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');
Dans la plupart des navigateurs modernes, il existe un type de données symbol primitive qui peut être utilisé pour créer une énumération. Cela garantira la sécurité de type de l’énumération car chaque valeur de symbole est garantie par JavaScript comme étant unique, c’est-à-dire Symbol() != Symbol()
. Par exemple:
const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});
Pour simplifier le débogage, vous pouvez ajouter une description aux valeurs d’énumération:
const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});
Sur GitHub vous pouvez trouver un wrapper qui simplifie le code requis pour initialiser l’énumération:
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
J'ai joué avec ça, car j'aime mes enums. =)
En utilisant Object.defineProperty
, je pense avoir mis au point une solution quelque peu viable.
Voici un jsfiddle: http://jsfiddle.net/ZV4A6/
En utilisant cette méthode, vous devriez (en théorie) pouvoir appeler et définir des valeurs enum pour tout objet, sans affecter les autres attributs de cet objet.
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
});
En raison de l'attribut writable:false
this devrait faites-le taper en toute sécurité.
Vous devriez donc pouvoir créer un objet personnalisé, puis appeler Enum()
dessus. Les valeurs attribuées commencent à 0 et augmentent par élément.
var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED; // == 0
EnumColors.BLUE; // == 1
EnumColors.GREEN; // == 2
EnumColors.YELLOW; // == 3
C’est un vieux problème que je connais, mais la façon dont il a depuis été implémenté via l’interface TypeScript est la suivante:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["Foo"] = 0] = "Foo";
MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));
Cela vous permet de consulter à la fois MyEnum.Bar
qui renvoie 1 et MyEnum[1]
qui renvoie "Barre" quel que soit l'ordre de déclaration.
La "syntaxe préférée" par la plupart des gens a déjà été énumérée ci-dessus. Cependant, il existe un problème majeur: les performances. Pas une seule des réponses ci-dessus n’est très performante et elles gonflent toutes la taille de votre code à l’extrême. Pour des performances réelles, une lecture facile du code et une réduction sans précédent de la taille du code par minification, c'est la bonne façon de faire des énumérations.
const ENUM_COLORENUM_RED = 0,
ENUM_COLORENUM_GREEN = 1,
ENUM_COLORENUM_BLUE = 2,
ENUMLEN_COLORENUM = 3;
// later on
if(currentColor == ENUM_COLORENUM_RED) {
// whatever
}
De plus, cette syntaxe permet une extension de classe claire et concise, comme indiqué ci-dessous.
(Longueur: 2 450 octets)
(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);
Certains diront peut-être que c'est moins pratique que d'autres solutions: cela prend beaucoup de temps, il faut beaucoup de temps pour écrire et ce n'est pas recouvert de la syntaxe sucre. Oui, ces personnes auraient raison si elles ne minifiaient pas leur code. Cependant, aucune personne raisonnable ne laisserait du code non miné dans le produit final. Pour cette minification, Closure Compiler est le meilleur que je n'ai pas encore trouvé. L'accès en ligne peut être trouvé ici . Le compilateur Closure est capable de prendre toutes ces données d’énumération et de les mettre en ligne, ce qui permet à votre javascript d’être exécuté super rapidement et d’être petit. Observer.
(Longueur: 605 octets)
'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);
Voyons maintenant quelle serait la taille du fichier équivalent sans aucune de ces énumérations.
Source sans ces énumérations (longueur: 1 973 octets (477 octets plus court!))
Réduit sans ces énumérations (longueur: 843 octets (238 octets plus long))
Comme on le voit, sans énumérations, le code source est plus court au prix d’un code plus petit et plus détaillé. Je ne sais pas pour vous, je suis sûr que je n'aime pas incorporer le code source dans le produit final, ce qui rend cette énumération bien supérieure au point de réduire les fichiers. Ajoutez à cela cette forme d'énumération étant bien plus rapide. En effet, cette forme de dénombrement est la voie à suivre.
Dans ES7 , vous pouvez créer un élégant ENUM en vous basant sur des attributs statiques:
class ColorEnum {
static RED = 0 ;
static GREEN = 1;
static BLUE = 2;
}
puis
if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}
L'avantage (d'utiliser une classe au lieu d'un objet littéral) est d'avoir une classe parent Enum
, alors tous vos énums vont extend cette classe.
class ColorEnum extends Enum {/*....*/}
TLDR: Ajouter cette classe à vos méthodes d’utilitaire et l’utiliser dans votre code, elle bloque le comportement Enum des langages de programmation traditionnels et génère des erreurs lorsque vous essayez d’accéder à un énumérateur inexistant ou d’ajouter/mettre à jour un énumérateur. Pas besoin de compter sur 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);
}
}
Puis créez des énumérations en instanciant la classe:
const roles = new Enum({
ADMIN: 'Admin',
USER: 'User',
});
Explication complète:
Une caractéristique très bénéfique des Enums que vous obtenez des langages traditionnels est qu’ils explosent (provoquent une erreur lors de la compilation) si vous essayez d’avoir accès à un énumérateur qui n’existe pas.
En plus de geler la structure d’enum simulée afin d’éviter l’ajout accidentel/malveillant de valeurs supplémentaires, aucune des autres réponses ne traite de cette caractéristique intrinsèque d’Enums.
Comme vous le savez probablement, l'accès à des membres non existants dans JavaScript renvoie simplement undefined
et ne fait pas exploser votre code. Les énumérateurs étant des constantes prédéfinies (c'est-à-dire les jours de la semaine), il ne devrait jamais y avoir de cas où un énumérateur devrait être indéfini.
Ne vous méprenez pas, le comportement de JavaScript consistant à renvoyer undefined
lors de l'accès à des propriétés non définies est en fait une fonctionnalité très puissante du langage, mais ce n'est pas une fonctionnalité que vous souhaitez lorsque vous essayez de vous moquer des structures Enum traditionnelles.
C'est là que brillent les objets proxy. Les procurations ont été normalisées dans la langue avec l'introduction de ES6 (ES2015). Voici la description de MDN:
L'objet Proxy est utilisé pour définir un comportement personnalisé pour les opérations fondamentales (recherche de propriétés, affectation, énumération, fonction Invocation, etc.).
Semblables à un proxy de serveur Web, les proxys JavaScript sont capables d’intercepter des opérations sur des objets (en utilisant des "pièges", appelez-les si vous le souhaitez) et vous permettent d’effectuer diverses vérifications, actions et/ou manipulations dans certains cas, arrêter complètement les opérations, ce qui est exactement ce que nous voulons faire si et quand nous essayons de référencer un énumérateur qui n’existe pas).
Voici un exemple artificiel qui utilise l'objet Proxy pour imiter Enums. Les énumérateurs de cet exemple sont des méthodes HTTP standard ("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'est-ce que c'est qu'un proxy?
Je me souviens que lorsque j'ai commencé à voir le proxy Word partout, cela ne me paraissait absolument pas logique pendant très longtemps. Si c'est votre cas en ce moment, je pense qu'un moyen facile de généraliser les procurations consiste à les considérer comme des logiciels, des institutions ou même des personnes jouant le rôle d'intermédiaire ou d'intermédiaire entre deux serveurs, des entreprises ou des personnes.
C'est la solution que j'utilise.
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;
}
Et vous définissez vos enums comme ceci:
var COLORS = defineEnum({
RED : {
value : 1,
string : 'red'
},
GREEN : {
value : 2,
string : 'green'
},
BLUE : {
value : 3,
string : 'blue'
}
});
Et voici comment vous accédez à vos 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
});
J'utilise généralement les deux dernières méthodes pour mapper des énumérations à partir d'objets message.
Quelques avantages de cette approche:
Quelques inconvénients:
Créez un objet littéral:
const Modes = {
DRAGGING: 'drag',
SCALING: 'scale',
CLICKED: 'click'
};
Si vous utilisez Backbone , vous pouvez obtenir gratuitement la fonctionnalité d’énumération complète (rechercher par id, nom, membres personnalisés) à l’aide de 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()
vos réponses sont beaucoup trop compliquées
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
J'ai modifié la solution 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);
}
}
Tester:
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
IE8 ne supporte pas la méthode freeze ().
Source: http://kangax.github.io/compat-table/es5/ , cliquez sur "Afficher les navigateurs obsolètes?" sur le dessus et vérifiez l’intersection des colonnes IE8 et Freeze.
J'ai utilisé ci-dessous dans mon projet de jeu actuel, car peu de clients utilisent encore IE8:
var CONST_WILD_TYPES = {
REGULAR: 'REGULAR',
EXPANDING: 'EXPANDING',
STICKY: 'STICKY',
SHIFTING: 'SHIFTING'
};
Nous pourrions aussi faire:
var CONST_WILD_TYPES = {
REGULAR: 'RE',
EXPANDING: 'EX',
STICKY: 'ST',
SHIFTING: 'SH'
};
ou même ceci:
var CONST_WILD_TYPES = {
REGULAR: '1',
EXPANDING: '2',
STICKY: '3',
SHIFTING: '4'
};
Le dernier, qui semble le plus efficace pour les chaînes, réduit votre bande passante totale si votre serveur et votre client échangent ces données.
Bien sûr, il est maintenant de votre devoir de vous assurer qu'il n'y a pas de conflits de données (RE, EX, etc., doivent être uniques, ainsi que 1, 2, etc., doivent être uniques). Notez que vous devez les conserver pour toujours pour assurer la compatibilité ascendante.
Affectation:
var wildType = CONST_WILD_TYPES.REGULAR;
Comparaison:
if (wildType === CONST_WILD_TYPES.REGULAR) {
// do something here
}
var ColorEnum = {
red: {},
green: {},
blue: {}
}
Vous n'avez pas besoin de vous assurer que vous n'affectez pas les numéros en double à différentes valeurs enum de cette façon. Un nouvel objet est instancié et affecté à toutes les valeurs enum.
Je suis venu avec this approche qui est calqué sur des enums en Java. Celles-ci sont sécurisées au type et vous pouvez donc effectuer des vérifications instanceof
.
Vous pouvez définir des énumérations comme ceci:
var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);
Days
fait maintenant référence à 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 mise en oeuvre:
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
}
})();
J'ai créé une classe Enum pouvant récupérer des valeurs ET des noms en O (1). Il peut également générer un tableau d'objets contenant tous les noms et toutes les valeurs.
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);
Vous pouvez l'initialiser comme ceci:
var enum1 = new Enum({
item1: 0,
item2: 1,
item3: 2
});
Pour récupérer une valeur (comme Enums en C #):
var val2 = enum1.item2;
Pour récupérer un nom pour une valeur (peut être ambigu lorsque vous mettez la même valeur pour différents noms)
var name1 = enum1.GetName(0); // "item1"
Pour obtenir un tableau avec chaque nom et valeur dans un objet:
var arr = enum1.GetObjArr();
Va générer:
[{ Name: "item1", Value: 0}, { ... }, ... ]
Vous pouvez également obtenir facilement les options de sélection html:
var html = enum1.GetSelectOptionsHTML();
Qui tient:
"<option value='0'>item1</option>..."
Vous pouvez faire quelque chose comme ça
var Enum = (function(foo) {
var EnumItem = function(item){
if(typeof item == "string"){
this.name = item;
} else {
this.name = item.name;
}
}
EnumItem.prototype = new String("DEFAULT");
EnumItem.prototype.toString = function(){
return this.name;
}
EnumItem.prototype.equals = function(item){
if(typeof item == "string"){
return this.name == item;
} else {
return this == item && this.name == item.name;
}
}
function Enum() {
this.add.apply(this, arguments);
Object.freeze(this);
}
Enum.prototype.add = function() {
for (var i in arguments) {
var enumItem = new EnumItem(arguments[i]);
this[enumItem.name] = enumItem;
}
};
Enum.prototype.toList = function() {
return Object.keys(this);
};
foo.Enum = Enum;
return Enum;
})(this);
var STATUS = new Enum("CLOSED","PENDING", { name : "CONFIRMED", ackd : true });
var STATE = new Enum("CLOSED","PENDING","CONFIRMED",{ name : "STARTED"},{ name : "PROCESSING"});
Comme défini dans cette bibliothèque . https://github.com/webmodule/foo/blob/master/foo.js#L217
Exemple complet https://Gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026
Même si seules les méthodes statiques (et non les propriétés statiques) sont prises en charge dans ES2015 (voir ici aussi, §15.2.2.2), curieusement, vous pouvez utiliser ce qui suit avec Babel avec le préréglage 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);
J'ai constaté que cela fonctionnait comme prévu, même pour tous les modules (par exemple, l'importation de l'énumération CellState
à partir d'un autre module) et également lorsque j'importais un module à l'aide de Webpack.
L'avantage de cette méthode par rapport à la plupart des autres réponses est que vous pouvez l'utiliser avec un vérificateur de type statique (par exemple, Flow ) et vous pouvez affirmer, au moment du développement à l'aide de la vérification de type statique, que vos variables, paramètres , etc. sont de la CellState
"enum" spécifique plutôt que d'une autre enum (qu'il serait impossible de distinguer si vous utilisiez des objets ou des symboles génériques).
Le code ci-dessus présente un inconvénient en ce qu'il permet de créer des objets supplémentaires de type CellState
(même si on ne peut pas les affecter aux champs statiques de CellState
car ils sont gelés). Néanmoins, le code ci-dessous plus raffiné offre les avantages suivants:
CellState
ne peut être crééla fonction values
qui renvoie toutes les instances de l'énumération n'a pas à créer la valeur de retour de la manière ci-dessus, manuelle (et sujette aux erreurs).
'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;
Au moment de l'écriture, octobre 2014 - voici donc une solution contemporaine. Je suis en train d’écrire la solution en tant que module de noeud et j’ai inclus un test utilisant Mocha et Chai, ainsi que underscoreJS. Vous pouvez facilement les ignorer, et prenez simplement le code Enum si vous le préférez.
Vu beaucoup de publications avec des bibliothèques trop compliquées, etc. La solution pour obtenir le support enum en Javascript est si simple qu'elle n'est vraiment pas nécessaire. Voici le code:
Fichier: enums.js
_ = require('underscore');
var _Enum = function () {
var keys = _.map(arguments, function (value) {
return value;
});
var self = {
keys: keys
};
for (var i = 0; i < arguments.length; i++) {
self[keys[i]] = i;
}
return self;
};
var fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));
var encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));
exports.fileFormatEnum = fileFormatEnum;
exports.encodingEnum = encodingEnum;
Et un test pour illustrer ce que cela vous donne:
fichier: 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');
});
});
});
Comme vous pouvez le constater, vous obtenez une fabrique Enum, vous pouvez obtenir toutes les clés simplement en appelant enum.keys, et vous pouvez faire correspondre les clés elles-mêmes à des constantes entières. Et vous pouvez réutiliser la fabrique avec différentes valeurs et exporter les énumérations générées à l'aide de l'approche modulaire de Node.
Encore une fois, si vous êtes juste un utilisateur occasionnel, ou dans le navigateur, etc., prenez simplement la partie usine du code, supprimant éventuellement la bibliothèque de soulignement si vous ne souhaitez pas l'utiliser dans votre code.
Un moyen simple et rapide serait:
var Colors = function(){
return {
'WHITE':0,
'BLACK':1,
'RED':2,
'GREEN':3
}
}();
console.log(Colors.WHITE) //this prints out "0"
Voici quelques manières d'implémenter TypeScript enums .
Le moyen le plus simple consiste simplement à effectuer une itération sur un objet, en ajoutant des paires clé-valeur inversées à l'objet. Le seul inconvénient est que vous devez définir manuellement la valeur pour chaque membre.
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
Et voici un lodash mixin pour créer une énumération utilisant une chaîne. Bien que cette version soit un peu plus complexe, elle effectue automatiquement la numérotation pour vous. Toutes les méthodes lodash utilisées dans cet exemple ont un équivalent JavaScript standard, vous pouvez donc facilement les changer si vous le souhaitez.
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
Voici comment TypeScript traduit sa enum
en Javascript:
var makeEnum = function(obj) {
obj[ obj['Active'] = 1 ] = 'Active';
obj[ obj['Closed'] = 2 ] = 'Closed';
obj[ obj['Deleted'] = 3 ] = 'Deleted';
}
À présent:
makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}
Au début, je ne savais pas pourquoi obj[1]
retournait 'Active'
, mais je me suis rendu compte que son opérateur mort simple - Assignment attribue une valeur, puis la renvoie:
obj['foo'] = 1
// => 1
Solution la plus simple:
var Status = Object.freeze({
"Connecting":0,
"Ready":1,
"Loading":2,
"Processing": 3
});
console.log(Status.Ready) // 1
console.log(Object.keys(Status)[Status.Ready]) // Ready
C'est facile à utiliser, je pense. 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);
METTRE À JOUR:
Il y a mes codes d'assistance (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"]));
Vous pouvez essayer ceci:
var Enum = Object.freeze({
Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
});
alert(Enum.Role.Supervisor);
alert(Enum.Color.GREEN);
var currentColor=0;
if(currentColor == Enum.Color.RED) {
alert('Its Red');
}
es7 way, (itérateur, gel), utilisation:
const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')
for (let name of ThreeWiseMen)
console.log(name)
// with a given key
let key = ThreeWiseMen.Melchior
console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)
for (let entry from key.enum)
console.log(entry)
// prevent alteration (throws TypeError in strict mode)
ThreeWiseMen.Me = 'Me too!'
ThreeWiseMen.Melchior.name = 'Foo'
code:
class EnumKey {
constructor(props) { Object.freeze(Object.assign(this, props)) }
toString() { return this.name }
}
export class Enum {
constructor(...keys) {
for (let [index, key] of keys.entries()) {
Object.defineProperty(this, key, {
value: new EnumKey({ name:key, index, enum:this }),
enumerable: true,
})
}
Object.freeze(this)
}
*[Symbol.iterator]() {
for (let key of Object.keys(this))
yield this[key]
}
toString() { return [...this].join(', ') }
}
Je l'avais fait il y a quelque temps en utilisant une combinaison de __defineGetter__
et __defineSetter__
ou defineProperty
en fonction de la version de JS.
Voici la fonction de génération d’énumération que j’ai réalisée: https://Gist.github.com/gfarrell/6716853
Vous l'utiliseriez comme ceci:
var Colours = Enum('RED', 'GREEN', 'BLUE');
Et cela créerait une chaîne immuable: int dictionary (une énumération).
J'ai écrit enumerationjs a une très petite bibliothèque pour résoudre le problème qui assure le type safe, autorise les constantes enum à hériter d'un prototype, garantit les constantes et les types enum immuables + beaucoup de petites fonctionnalités. Cela permet de refactoriser beaucoup de code et de déplacer une logique dans la définition d’enum. Voici un exemple :
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
est fondamentalement une usine.
Guide entièrement documenté disponible ici. J'espère que cela t'aides.
J'aime vraiment ce que @Duncan a fait ci-dessus, mais je n'aime pas gâcher l'espace de la fonction Object globale avec Enum, alors j'ai écrit ce qui suit:
function mkenum_1()
{
var o = new Object();
var c = -1;
var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};
for (i in arguments) {
var e = arguments[i];
if ((!!e) & (e.constructor == Object))
for (j in e)
f(j, (c=e[j]));
else
f(e, ++c);
}
return Object.freeze ? Object.freeze(o) : o;
}
var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');
console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);
// Output is:
// MED := 1
// LRG := 100
@Stijin a également une solution soignée (faisant référence à son blog) qui inclut des propriétés sur ces objets. J'ai aussi écrit du code pour cela, que j'inclurai ensuite.
function mkenum_2(seed)
{
var p = {};
console.log("Seed := " + seed);
for (k in seed) {
var v = seed[k];
if (v instanceof Array)
p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
else
p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
}
seed.properties = p;
return Object.freeze ? Object.freeze(seed) : seed;
}
Cette version génère une liste de propriétés supplémentaire permettant la conversion de noms conviviaux et des codes abrégés. J'aime cette version car il n'est pas nécessaire de dupliquer la saisie de données dans les propriétés car le code le fait pour vous.
var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });
Ces deux éléments peuvent être combinés en une seule unité de traitement, mkenum, (consommer des énumérations, attribuer des valeurs, créer et ajouter une liste de propriétés). Cependant, comme j’ai déjà passé beaucoup trop de temps à ce sujet aujourd’hui, je laisserai cette combinaison comme un exercice pour le cher lecteur.
Vous pouvez utiliser Object.prototype.hasOwnProperty ()
var findInEnum,
colorEnum = {
red : 0,
green : 1,
blue : 2
};
// later on
findInEnum = function (enumKey) {
if (colorEnum.hasOwnProperty(enumKey)) {
return enumKey+' Value: ' + colorEnum[enumKey]
}
}
alert(findInEnum("blue"))
Qu'est-ce qu'une énumération dans my opinion: C'est un objet immuable qui est toujours accessible et vous pouvez comparer des éléments les uns avec les autres, mais les éléments ont des propriétés/méthodes communes, mais les objets eux-mêmes ou les valeurs ne peuvent pas être modifiés et ils le sont. instancié qu'une seule fois.
Les enums sont notamment utilisés pour comparer les types de données, les paramètres, les actions à entreprendre/répondre, etc.
Donc, pour cela, vous avez besoin d’objets avec la même instance afin de pouvoir vérifier s’il s’agit d’un type d’énumération if(something instanceof enum)
répondez toujours de la même manière.
Dans mon cas, il s'agit de comparer les valeurs de types de données, mais il peut s'agir de n'importe quoi, de la modification de blocs dans une direction opposée dans un jeu en 3D à la transmission de valeurs à un registre de type d'objet spécifique.
En gardant à l’esprit que c’est du javascript qui ne fournit pas un type d’énumération fixe, vous finissez toujours par créer votre propre implémentation et, comme le montre ce fil de discussion, il existe de nombreuses implémentations sans qu'aucune ne soit absolument correcte.
C'est ce que j'utilise pour Enums. Comme les énumérations sont immuables (ou devraient l'être au moins), je gèle les objets pour qu'ils ne puissent pas être manipulés facilement.
Les énumérations peuvent être utilisées par EnumField.STRING et elles ont leurs propres méthodes qui fonctionneront avec leurs types . Pour tester si quelque chose est passée à un objet, vous pouvez utiliser if(somevar instanceof EnumFieldSegment)
Ce n'est peut-être pas la solution la plus élégante et je suis ouvert aux améliorations, mais ce type d'enum immuable (à moins que vous ne le libériez) est exactement le cas d'utilisation dont j'avais besoin.
Je réalise aussi que j'aurais pu écraser le prototype avec un {} mais mon esprit fonctionne mieux avec ce format ;-) me tire dessus.
/**
* simple parameter object instantiator
* @param name
* @param value
* @returns
*/
function p(name,value) {
this.name = name;
this.value = value;
return Object.freeze(this);
}
/**
* EnumFieldSegmentBase
*/
function EnumFieldSegmentBase() {
this.fieldType = "STRING";
}
function dummyregex() {
}
dummyregex.prototype.test = function(str) {
if(this.fieldType === "STRING") {
maxlength = arguments[1];
return str.length <= maxlength;
}
return true;
};
dummyregexposer = new dummyregex();
EnumFieldSegmentBase.prototype.getInputRegex = function() {
switch(this.fieldType) {
case "STRING" : return dummyregexposer;
case "INT": return /^(\d+)?$/;
case "DECIMAL2": return /^\d+(\.\d{1,2}|\d+|\.)?$/;
case "DECIMAL8": return /^\d+(\.\d{1,8}|\d+|\.)?$/;
// boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its false, otherwise lets see what Boolean produces
case "BOOLEAN": return dummyregexposer;
}
};
EnumFieldSegmentBase.prototype.convertToType = function($input) {
var val = $input;
switch(this.fieldType) {
case "STRING" : val = $input;break;
case "INT": val==""? val=0 :val = parseInt($input);break;
case "DECIMAL2": if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break;
case "DECIMAL8": if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break;
// boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its false, otherwise lets see what Boolean produces
case "BOOLEAN": val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf())) ;break;
}
return val;
};
EnumFieldSegmentBase.prototype.convertToString = function($input) {
var val = $input;
switch(this.fieldType) {
case "STRING": val = $input;break;
case "INT": val = $input+"";break;
case "DECIMAL2": val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break;
case "DECIMAL8": val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break;
case "BOOLEAN": val = $input ? "true" : "false" ;break;
}
return val;
};
EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) {
var val = false;
switch(this.fieldType) {
case "STRING": val = ($val1===$val2);break;
case "INT": val = ($val1===$val2);break;
case "DECIMAL2": val = ($val1.comparedTo($val2)===0);break;
case "DECIMAL8": val = ($val1.comparedTo($val2)===0);break;
case "BOOLEAN": val = ($val1===$val2);break;
}
return val;
};
/**
* EnumFieldSegment is an individual segment in the
* EnumField
* @param $array An array consisting of object p
*/
function EnumFieldSegment() {
for(c=0;c<arguments.length;c++) {
if(arguments[c] instanceof p) {
this[arguments[c].name] = arguments[c].value;
}
}
return Object.freeze(this);
}
EnumFieldSegment.prototype = new EnumFieldSegmentBase();
EnumFieldSegment.prototype.constructor = EnumFieldSegment;
/**
* Simple enum to show what type of variable a Field type is.
* @param STRING
* @param INT
* @param DECIMAL2
* @param DECIMAL8
* @param BOOLEAN
*
*/
EnumField = Object.freeze({STRING: new EnumFieldSegment(new p("fieldType","STRING")),
INT: new EnumFieldSegment(new p("fieldType","INT")),
DECIMAL2: new EnumFieldSegment(new p("fieldType","DECIMAL2")),
DECIMAL8: new EnumFieldSegment(new p("fieldType","DECIMAL8")),
BOOLEAN: new EnumFieldSegment(new p("fieldType","BOOLEAN"))});
class Enum {
constructor (...vals) {
vals.forEach( (val, i) => {
Object.defineProperty(this, val.toUpperCase(), {
get () {
return i;
},
set (val) {
const enum_val = "CONSTANT";
// generate TypeError associated with attempting to change the value of a constant
enum_val = val;
}
});
});
}
}
Exemple d'utilisation:
const COLORS = new Enum("red", "blue", "green")
console.log(COLORS.RED) // 0
console.log(COLORS.GREEN) // 2
Lisez toutes les réponses et n’avez trouvé aucune solution non verbeuse et DRY. J'utilise ce one-liner:
const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});
il génère un objet avec des valeurs lisibles par l'homme:
{
DRAW: 'DRAW',
SCALE: 'SCALE',
DRAG: 'DRAG'
}
La solution Alien est de rendre les choses aussi simples que possible:
Si le mot clé enum est simplement réservé mais non implémenté dans votre javascript, définissez les éléments suivants:
const enumerate = spec => spec.split(/\s*,\s*/)
.reduce((e, n) => Object.assign(e,{[n]:n}), {})
Maintenant, vous pouvez facilement l'utiliser
const kwords = enumerate("begin,end, procedure,if")
console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)
Je ne vois aucune raison de rendre les valeurs énumérées des variables explicites. Les scripts sont quand même morphiques et cela ne fait aucune différence si une partie de votre code est une chaîne ou un code valide. Ce qui compte vraiment, c’est que vous n’avez pas besoin de traiter avec des tonnes de guillemets chaque fois que vous les utilisez ou les définissez.
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });
Vous n'avez pas besoin de spécifier un id, vous pouvez simplement utiliser un objet vide pour comparer des énumérations.
if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
EDIT: Si vous souhaitez sérialiser l'objet (en JSON par exemple), vous devez à nouveau id.
Cette réponse est une approche alternative pour des circonstances spécifiques. J'avais besoin d'un ensemble de constantes de masque binaire basé sur des sous-valeurs d'attribut (cas où une valeur d'attribut est un tableau ou une liste de valeurs). Il englobe l'équivalent de plusieurs énumérations qui se chevauchent.
J'ai créé une classe pour stocker et générer les valeurs de masque de bits. Je peux ensuite utiliser les valeurs de masque binaire pseudo-constantes de cette manière pour tester, par exemple, si du vert est présent dans une valeur RVB:
if (value & Ez.G) {...}
Dans mon code, je crée une seule instance de cette classe. Il ne semble pas y avoir de moyen propre de faire cela sans instancier au moins une instance de la classe. Voici le code de génération de la déclaration de classe et du bitmask:
class Ez {
constructor() {
let rgba = ["R", "G", "B", "A"];
let rgbm = rgba.slice();
rgbm.Push("M"); // for feColorMatrix values attribute
this.createValues(rgba);
this.createValues(["H", "S", "L"]);
this.createValues([rgba, rgbm]);
this.createValues([attX, attY, attW, attH]);
}
createValues(a) { // a for array
let i, j;
if (isA(a[0])) { // max 2 dimensions
let k = 1;
for (i of a[0]) {
for (j of a[1]) {
this[i + j] = k;
k *= 2;
}
}
}
else { // 1D array is simple loop
for (i = 0, j = 1; i < a.length; i++, j *= 2)
this[a[i]] = j;
}
}
Le tableau 2D correspond à l'attribut valeurs SVG feColorMatrix, qui est une matrice 4x5 de RGBA par RGBAM, où M est un multiplicateur. Les propriétés Ez résultantes sont Ez.RR, Ez.RG, etc.
Depuis ES6, vous ne pouvez pas créer d’Enums en tant que tel, mais vous pouvez utiliser une syntaxe plus élégante, pouvant être complétée automatiquement, du moins sur VSCode:
class MyEnum {
const A = '1'
const B = '2'
const C = '3'
}
Sur le plan positif, vous pouvez mettre ce que vous voulez dans la variable const
comme dans les autres réponses. De plus, dans Node, vous pouvez l'exporter en tant que partie d'un module tout en conservant un nom significatif.
import { MyEnum } from './my-enum'
console.log(MyEnum.B)
J'espère que cela t'aides.
Vous pouvez utiliser une fonction simple pour inverser les clés et les valeurs. Cela fonctionnera également avec les tableaux car il convertit les chaînes entières numériques en nombres. Le code est petit, simple et réutilisable pour ce cas d’utilisation.
var objInvert = function (obj) {
var invert = {}
for (var i in obj) {
if (i.match(/^\d+$/)) i = parseInt(i,10)
invert[obj[i]] = i
}
return invert
}
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL',
'BOSSA-NOVA','POP','INDIE']))
console.log(musicStyles)