Question Quelle est la syntaxe préférée pour définir des énumérations en JavaScript?


Quelle est la syntaxe préférée pour définir des é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 y a-t-il un idiome plus préférable?


1675
2017-11-13 19:09


origine


Réponses:


Depuis 1.8.5 il est possible de sceller et de geler l'objet, donc définissez ce qui suit:

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

ou

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

et voilà! JS enums.

Cependant, cela ne vous empêche pas d'attribuer une valeur indésirable à une variable, ce qui est souvent l'objectif principal d'enums:

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

Une façon d'assurer un degré plus élevé de sécurité de type (avec enums ou autrement) est d'utiliser un outil comme Manuscrit ou Couler.

La source

Les citations ne sont pas nécessaires mais je les ai conservées pour la cohérence.


539
2018-02-18 11:03



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

Cela dit, comme les valeurs n'ont pas d'importance (vous avez utilisé 0, 1, 2), j'utiliserais une chaîne significative si jamais vous vouliez afficher la valeur actuelle.


583
2017-11-13 19:13



METTRE À JOUR: Merci pour tous les upvotes tout le monde, mais je ne pense pas que ma réponse ci-dessous est la meilleure façon d'écrire des enums en Javascript plus. Voir 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 faire les objets de valeurs, ainsi vous pouvez 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 c'est un langage dynamique, il est même possible d'ajouter des valeurs enum à l'ensemble plus tard:

// 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 là que par commodité. De même, le nom de la propriété size n'a pas besoin d'être codé en dur, mais peut également être défini dynamiquement. Supposons que vous ne connaissiez que le nom de votre nouvelle valeur enum, vous pouvez toujours 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 juste comme une carte ou une hashtable. Un ensemble de paires nom-valeur. Vous pouvez les parcourir ou les manipuler autrement sans en savoir beaucoup à l'avance.

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 btw, si vous êtes intéressé par les espaces de noms, vous voudrez peut-être jeter un oeil à ma solution pour l'espace de noms simple et puissant et la gestion des dépendances pour javascript: Forfaits JS


477
2018-03-04 22:31



Bottom line: Vous ne pouvez pas.

Vous pouvez faire semblant, mais vous n'obtiendrez pas la sécurité de type. Typiquement ceci est fait en créant un dictionnaire simple des valeurs de chaîne mappées aux 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 accidentellement redéfinir votre énumérant, ou accidentellement avoir des valeurs énumérées 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 fonctionnerait-il pas pour vous empêcher de passer du lundi au jeudi? - Fry Quad

Absolument, Object.freeze réparerait 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 n'a pas vraiment existé.

Maintenant .... maintenant il ouvre un peu très possibilités intéressantes.

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

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

Bien qu'il ne corresponde probablement pas à toutes les utilisations valables des énumérations, cela va très loin.


79
2017-08-21 20:56



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

En faisant cela, les constantes peuvent être traitées de la manière habituelle (YesNo.YES, Color.GREEN) et elles reçoivent 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, il ne fonctionne pas correctement sur IE: S ... devrait coller 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');

46
2017-07-13 00:28



Dans la plupart des navigateurs modernes, il y a un symbole type de données primitif pouvant être utilisé pour créer une énumération. Il assurera la sécurité de type de l'énumération 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émonstration de Plunker

Sur GitHub vous pouvez trouver un wrapper qui simplifie le code requis pour initialiser l'enum:

const color = new Enum("RED", "BLUE")

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE

41
2018-05-05 16:32



J'ai joué avec cela, car j'aime mes enums. =)

En utilisant Object.defineProperty Je pense que j'ai trouvé une solution assez 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 n'importe quel 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 ce devrait Faites-en un type sûr.

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

23
2017-08-21 10:34



C'est un ancien que je connais, mais la façon dont il a été implémenté depuis 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 les deux MyEnum.Bar qui renvoie 1, et MyEnum[1] qui renvoie "Bar" indépendamment de l'ordre de déclaration.


17
2018-06-24 16:11



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 énumérations 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 énumérations:

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 2 dernières méthodes pour mapper des énumérations à partir d'objets de message.

Quelques avantages à cette approche:

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

Quelques inconvénients:

  • Un peu de gestion de la mémoire en désordre, car je garde les références aux enums
  • Toujours pas de sécurité de type

15
2018-05-15 09:26



Si vous utilisez Colonne vertébrale, vous pouvez obtenir une fonctionnalité complète enum (recherche par identifiant, nom, membres personnalisés) gratuitement en utilisant 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()

12
2018-04-24 14:04



Dans ES7 , vous pouvez faire un ENUM élégant 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 (d'utiliser la classe au lieu de l'objet littéral) est d'avoir une classe parente Enumalors tous vos Enums seront s'étend cette classe.

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

11
2018-01-06 07:07