Enums en JavaScript?

Quelle est la syntaxe préférée pour définir des énoncés en JavaScript? Quelque chose comme:

my.namespace.ColorEnum = { RED : 0, GREEN : 1, BLUE : 2 } // later on if(currentColor == my.namespace.ColorEnum.RED) { // whatever } 

Ou est-ce qu'il y a un idiome plus préférable?

Ce n'est pas une grande réponse, mais je dirais que ça marche très bien, personnellement

Cela dit, peu importe les valeurs (vous avez utilisé 0, 1, 2), j'utiliserais une chaîne significative au cas où vous auriez voulu afficher la valeur actuelle.

MISE À JOUR : Merci pour tous les commentaires de tous, mais je ne pense pas que ma réponse ci-dessous est la meilleure façon d'écrire des énoncés en Javascript. Voir la publication de mon blog pour plus de détails: Enums en 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); } } 

Alternativement, vous pouvez créer les objets des valeurs, afin que vous puissiez 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); } 

Dans Javascript, comme il s'agit d'un langage dynamique, il est même possible d'ajouter des valeurs d'énumération à l'ensemble ultérieur:

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

Rappelez-vous, les champs de l'énumération (valeur, nom et code dans cet exemple) ne sont pas nécessaires pour la vérification d'identité et ne sont disponibles que par commodité. En outre, le nom de la propriété taille n'a pas besoin d'être codé en dur, mais peut également être configuré de manière dynamique. Supposons donc que vous connaissiez le nom de votre nouvelle valeur d'énumération, vous pouvez l'ajouter sans problème:

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

Bien sûr, cela signifie que certaines hypothèses ne peuvent plus être faites (cette valeur représente l'ordre correct pour la taille, par exemple).

Rappelez-vous, en Javascript, un objet est comme une carte ou un hashtable. Un ensemble de paires nom-valeur. Vous pouvez les traverser ou les manipuler sans les connaître avant de les connaître.

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

Et bien, si vous êtes intéressé par des espaces de noms, vous voudrez peut-être jeter un oeil à ma solution pour un espace de noms simple et puissant et une gestion de dépendance pour javascript: Packages JS

Je ne peux pas poster de commentaire à la réponse, alors je suppose que je dérangerait le fil puisqu'il apparaîtrait dans Google.

Depuis 1.8.5, il est possible de sceller et de congeler l'objet, alors définissez ce qui suit 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 voila! JS enums;)

Source: https://developer.mozilla.org/fr/JavaScript/Reference/Global_Objects/Object/freeze

Les citations de l'OIM ne sont pas nécessaires, mais je les ai conservées pour la cohérence.

En bout de ligne: vous ne pouvez pas.

Vous pouvez le fausser, mais vous n'obtiendrez pas la sécurité du type. Généralement, cela se fait 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 énorme ou, accidentellement, avoir des valeurs d'énumérant en double. Par exemple:

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

modifier

Qu'en est-il de Object.freeze d'Artur Czajka? Est-ce que cela ne vous empêcherait pas de passer du lundi au jeudi? – Fry Quad

Absolument, Object.freeze totalement le problème dont je me suis plaint. Je voudrais rappeler à tous que lorsque j'ai écrit ce qui précède, Object.freeze pas vraiment.

Maintenant … maintenant, cela ouvre des possibilités très intéressantes.

Modifier 2
Voici une très bonne bibliothèque pour créer des énoncés.

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

Bien qu'il ne corresponde pas à toutes les utilisations valables des énigmes, 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 énoncés:

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

Ce faisant, les constantes peuvent être accessibles 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 */ }; 

Modifier – petite amélioration – maintenant avec varargs: (malheureusement, cela ne fonctionne pas correctement sur 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'); 

J'ai joué avec ça, car j'aime mes énigmes. =)

Utilisation de Object.defineProperty Je pense que j'ai trouvé 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 d'énumération pour n'importe quel objet, sans affecter d'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 cela devrait le rendre sécurisé.

Donc, vous devriez pouvoir créer un objet personnalisé, puis appeler Enum() sur celui-ci. Les valeurs assigné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 

Dans la plupart des navigateurs modernes, il existe un type de données symboles primaires qui peut être utilisé pour créer un énumération. Cela garantira le type de sécurité de l'énoncé car chaque valeur de symbole est garantie par JavaScript pour être 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")}); 

Démo Plunker

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 

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 énigmes 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 énoncés:

 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 habituellement les 2 dernières méthodes pour cartographier des énoncés à partir d'objets de message.

Quelques avantages à cette approche:

  • Facile à déclarer enums
  • Facile d'accès à vos énigmes
  • Vos énoncés peuvent être des types complexes
  • La classe Enum a une mise en cache associative si vous utilisez BeByValue beaucoup

Quelques inconvénients:

  • Une gestion de la mémoire désordonnée se déroule là-bas, car je garde les références aux énigmes
  • Encore aucun type de sécurité

C'est un ancien que je connais, mais la façon dont il a été implémenté via l'interface TypeScript est:

 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 rechercher à la fois MyEnum.Bar qui renvoie 1, et MyEnum[1] qui renvoie "Bar" indépendamment de l'ordre de déclaration.

Si vous utilisez Backbone , vous pouvez obtenir une fonctionnalité d'énumération intégrale (trouver par identifiant, nom, membres personnalisés) gratuitement à 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() 

Créer un objet littéral:

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

J'ai modifié la solution d'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 

Vos réponses sont 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 

Dans ES7 , vous pouvez faire une ENUM élégante en s'appuyant sur des attributs statiques:

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

puis

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

L'avantage (de l'utilisation de la classe à la place de l'objet littéral) est d'avoir une classe de classe Enum alors tous vos Enums vont étendre cette classe.

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

IE8 ne prend pas en charge la méthode freeze ().
Source: http://kangax.github.io/compat-table/es5/ , cliquez sur "Afficher les navigateurs obsolètes?" En haut, et vérifiez IE8 et geler l'intersection col ligne.

Dans mon projet de jeu actuel, j'ai utilisé ci-dessous, car peu de clients utilisent toujours IE8:

 var CONST_WILD_TYPES = { REGULAR: 'REGULAR', EXPANDING: 'EXPANDING', STICKY: 'STICKY', SHIFTING: 'SHIFTING' }; 

Nous pourrions aussi:

 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, semble le plus efficace pour la chaîne, il réduit votre bande passante totale si vous avez un serveur et un client échangent ces données.
Bien sûr, il est maintenant de votre devoir de s'assurer qu'il n'y ait pas de conflit dans les données (RE, EX, etc. doivent être uniques, aussi 1, 2, etc. devraient être uniques). Notez que vous devez maintenir ces conditions pour une compatibilité ascendante.

Affectation:

 var wildType = CONST_WILD_TYPES.REGULAR; 

Comparaison:

 if (wildType === CONST_WILD_TYPES.REGULAR) { // do something here } 

J'ai fait une classe Enum qui peut récupérer des valeurs ET des noms à O (1). Il peut également générer un tableau d'objets contenant tous les noms et 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 init'd 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 lors de 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(); 

Générera:

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

Vous pouvez également obtenir les options de sélection html facilement:

 var html = enum1.GetSelectOptionsHTML(); 

Qui tient:

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

Une façon rapide et simple serait:

 var Colors = function(){ return { 'WHITE':0, 'BLACK':1, 'RED':2, 'GREEN':3 } }(); console.log(Colors.WHITE) //this prints out "0" 

Je suis venu avec cette approche qui est inspirée des énoncés en Java. Ce type est sécurisé et vous pouvez également effectuer des contrôles d' instanceof contrôle.

Vous pouvez définir des énoncés comme celui-ci:

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

Days se réfèrent maintenant à l'énoncé des Days :

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

À partir de l'écriture, octobre 2014 – donc, voici une solution contemporaine. Je suis en train d'écrire la solution en tant que module de noeud, et j'ai inclus un test en utilisant Mocha et Chai, ainsi que le soulignéJS. Vous pouvez facilement les ignorer, et prenez le code Enum si vous le préférez.

J'ai vu beaucoup de publications avec des bibliothèques trop compliquées, etc. La solution pour obtenir un soutien enum en JavaScript est si simple qu'il 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 qu'il 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 voir, vous obtenez une usine Enum, vous pouvez obtenir toutes les clés simplement en appelant enum.keys, et vous pouvez associer les clés elles-mêmes aux constantes entières. Et vous pouvez réutiliser l'usine avec des valeurs différentes et exporter les Enums générés en utilisant l'approche modulaire de Node.

Encore une fois, si vous êtes juste un utilisateur occasionnel, ou dans le navigateur, etc., prenez la partie d'usine du code, en supprimant éventuellement la bibliothèque de soulignement si vous ne souhaitez pas l'utiliser dans votre code.

Voici quelques façons différentes de mettre en œuvre les enples TypeScript .

La manière la plus simple consiste à simplement itérer sur un objet, en ajoutant des paires de valeurs-clés 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 

And here's a lodash mixin to create an enum using a string. While this version is a little bit more involved, it does the numbering automatically for you. All the lodash methods used in this example have a regular JavaScript equivalent, so you can easily switch them out if you want.

 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 

I've just published an NPM package gen_enum allows you to create Enum data structure in Javascript quickly:

 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 

One nice thing about this little tool is in modern environment (including nodejs and IE 9+ browsers) the returned Enum object is immutable.

For more information please checkout https://github.com/greenlaw110/enumjs

Updates

I obsolete gen_enum package and merge the function into constjs package, which provides more features including immutable objects, JSON string deserialization, string constants and bitmap generation etc. Checkout https://www.npmjs.com/package/constjs for more information

To upgrade from gen_enum to constjs just change the statement

 var genEnum = require('gen_enum'); 

à

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

You can do something like this

 function Enum(){ this.add.apply(this,arguments); } Enum.prototype.add = function(){ for (var i in arguments) { this[arguments[i]] = new String(arguments[i]); } }; Enum.prototype.toList = function(){ return Object.keys(this) }; var STATUS = new Enum("CLOSED","PENDING"); var STATE = new Enum("CLOSED","PENDING"); STATE.CLOSED === STATUS.CLOSED // false; STATE.CLOSED === "CLOSED" // false; STATE.CLOSED.toString() === "CLOSED" // true; 

As defined in this library. https://github.com/webmodule/foo/blob/master/foo.js#L217

Even though only static methods (and not static properties) are supported in ES2015 (see here as well, §15.2.2.2), curiously you can use the below with Babel with the es2015 preset:

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

I found this to be working as expected even across modules (eg importing the CellState enum from another module) and also when I import a module using Webpack.

The advantage this method has over most other answers is that you can use it alongside a static type checker (eg Flow ) and you can assert, at development time using static type checking, that your variables, parameters, etc. are of the specific CellState "enum" rather than some other enum (which would be impossible to distinguish if you used generic objects or symbols).

mettre à jour

The above code has a deficiency in that it allows one to create additional objects of type CellState (even though one can't assign them to the static fields of CellState since it's frozen). Still, the below more refined code offers the following advantages:

  1. no more objects of type CellState may be created
  2. you are guaranteed that no two enum instances are assigned the same code
  3. utility method to get the enum back from a string representation
  4. the values function that returns all instances of the enum does not have to create the return value in the above, manual (and error-prone) way.

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

I had done it a while ago using a mixture of __defineGetter__ and __defineSetter__ or defineProperty depending on the JS version.

Here's the enum generating function I made: https://gist.github.com/gfarrell/6716853

You'd use it like this:

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

And it would create an immutable string:int dictionary (an enum).

 var ColorEnum = { red: {}, green: {}, blue: {} } 

You don't need to make sure you don't assign duplicate numbers to different enum values this way. A new object gets instantiated and assigned to all enum values.

It's easy to use, I think. https://stackoverflow.com/a/32245370/4365315

 var A = {a:11, b:22}, enumA = new TypeHelper(A); if(enumA.Value === Ab || 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:

There is my helper codes( 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"])); 

I wrote enumerationjs a very tiny library to address the issue which ensures type safety , allow enum constants to inherit from a prototype , guaranties enum constants and enum types to be immutable + many little features. It allows to refactor a lot of code and move some logic inside the enum definition. Here is an example :

 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 is basically a factory.

Fully documented guide available here. J'espère que cela t'aides.

Simplest solution:

Create

 var Status = Object.freeze({ "Connecting":0, "Ready":1, "Loading":2, "Processing": 3 }); 

Get Value

 console.log(Status.Ready) // 1 

Get Key

 console.log(Object.keys(Status)[Status.Ready]) // Ready 

Really like what @Duncan did above, but I don't like mucking up global Object function space with Enum, so I wrote the following:

 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 also has a neat solution (referring to his blog) which includes properties on these objects. I wrote some code for that, too, which I'm including next.

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

This version produces an additional property list allowing friendly name conversion and short codes. I like this version because one need not duplicate data entry in properties as the code does it for you.

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

These two can be combined into a single processing unit, mkenum, (consume enums, assign values, create and add property list). However, as I've spent far too much time on this today already, I will leave the combination as an exercise for the dear reader.

What is an enum in my opinion: It's an immutable object that is always accessible and you can compare items with eachother, but the items have common properties/methods, but the objects themselves or the values cannot be changed and they are instantiated only once.

Enums are imho used for comparing datatypes, settings, actions to take/reply things like that.

So for this you need objects with the same instance so you can check if it is a enum type if(something instanceof enum) Also if you get an enum object you want to be able to do stuff with it, regardless of the enum type, it should always respond in the same way.

In my case its comparing values of datatypes, but it could be anything, from modifying blocks in facing direction in a 3d game to passing values on to a specific object type registry.

Keeping in mind it is javascript and doesn't provide fixed enum type, you end up always making your own implementation and as this thread shows there are legions of implementations without one being the absoulte correct.


This is what I use for Enums. Since enums are immutable(or should be at least heh) I freeze the objects so they can't be manipulated easely.

The enums can be used by EnumField.STRING and they have their own methods that will work with their types. To test if something passed to an object you can use if(somevar instanceof EnumFieldSegment)

It may not be the most elegant solution and i'm open for improvements, but this type of immutable enum(unless you unfreeze it) is exactly the usecase I needed.

I also realise I could have overridden the prototype with a {} but my mind works better with this format 😉 shoot me.

 /** * 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"))});