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

2082

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?

David Citron
la source
132
Ne pas utiliser 0comme numéro d'énumération. À moins qu'il ne soit utilisé pour quelque chose qui n'a pas été défini. JS les traite false || undefined || null || 0 || "" || '' || NaNtous comme la même valeur lorsqu'ils sont comparés à l'aide ==.
matsko
153
@matsko n'est-ce pas juste un argument contre l'utilisation de ==?
sdm350
6
0 == nullrenvoie faux
mcont
11
Mais false == 0et +null == 0(et les conversions en nombres se produisent parfois lorsque vous ne vous y attendez pas), tandis que null == undefinedtrop, et +undefinedc'est NaN(cependant NaN != NaN).
sanderd17
46
La matrice de double égalité est plus déroutante que la mise en forme automatique de Microsoft Word
aaaaaa

Réponses:

896

Depuis la version 1.8.5, il est possible de sceller et de geler l'objet, définissez donc ce qui précède comme:

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

ou

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

et le tour est joué! JS énumère.

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

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 des énumérations ou autre) consiste à utiliser un outil comme TypeScript ou Flow .

La source

Les citations ne sont pas nécessaires mais je les ai gardées pour plus de cohérence.

Artur Czajka
la source
6
Selon Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ), il s'applique à Firefox 4, IE 9, Opera 11.60 et je sais que cela fonctionne dans Chrome.
Artur Czajka
77
Ceci est la réponse en ce moment en 2012. Plus simple: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Vous n'avez pas besoin de spécifier un identifiant, vous pouvez simplement utiliser un objet vide pour comparer les énumérations. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas
34
Pour des if (Object.freeze) { Object.freeze(DaysEnum); }
raisons de
17
Je voudrais souligner que faire ({ monday: {}, etc. signifie que si vous convertissez cet objet en JSON via stringify, vous obtiendrez [{"day": {}}]ce qui ne fonctionnera pas.
jcollum
10
@Supuhstar Mon opinion sur cette question est maintenant différente. N'utilisez pas freeze (), c'est complètement inutile et c'est une perte de temps de faire des choses "stupides". Si vous souhaitez exposer une ENUM, exposer simplement ceci: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Comparer des objets comme dans mon commentaire précédent est BEAUCOUP PLUS LENT que de comparer des nombres.
Gabriel Llamas
608

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

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

Gareth
la source
377
Cela a été indiqué dans une autre réponse, mais comme cette réponse est la réponse acceptée, je posterai ceci ici. La solution de l'OP est correcte. Ce sera encore mieux s'il est utilisé avec Object.freeze(). Cela empêchera tout autre code de modifier les valeurs de l'énumération. Exemple:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth
5
@TolgaE merci pour cette bibliothèque! Cela m'a inspiré non seulement à le réduire au strict minimum, mais également à ajouter quelques fonctionnalités! J'ai fourré le vôtre et je l'ai mis ici: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar
3
@Supuhstar C'est super! Je suis content que vous ayez pu l'utiliser .. N'hésitez pas à faire une pull request si vous vouliez la fusionner dans cette bibliothèque, alors je pourrai mettre à jour la bibliothèque npm
Tolga E
2
Si quelqu'un est intéressé, j'ai implémenté des énumérations de type sécurisé similaires à la façon dont elles sont en Java. Cela signifie que vous pouvez effectuer des instanceofvérifications. Par exemple ColorEnum.RED instanceof ColorEnum(retourne true). Vous pouvez également résoudre une instance à partir d'un nom ColorEnum.fromName("RED") === ColorEnum.RED(retourne true). Chaque instance a également .name()une .ordinal()méthode et une , et l'énumération elle-même a une values()méthode qui retourne un tableau de toutes les constantes.
Vivin Paliath
3
Je ne suis pas sûr d'être d'accord avec la suggestion de "chaîne significative". Les énumérations ne doivent pas être considérées comme des chaînes ou des nombres; ce sont des types de données abstraits. Il ne devrait pas être possible de "sortir la valeur actuelle" sans une méthode d'assistance. En Java et .NET, c'est la ToString()méthode. Nous, les développeurs JS, sommes déjà trop dépendants des choses qui "fonctionnent"! En outre, on devrait pouvoir rapidement switchsur une énumération. La comparaison des chaînes est plus lente que la comparaison des nombres, vous obtiendrez donc des switchperformances légèrement inférieures si vous utilisez des chaînes au lieu d'entiers.
Rabadash8820
501

MISE À JOUR

Merci pour tous les votes positifs, 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: énumère 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 des objets de valeurs, vous pouvez donc 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 des valeurs d'énumération à l'ensemble plus tard:

// 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 des raisons de commodité. De plus, le nom de la propriété size elle-même n'a pas besoin d'être codé en dur, mais peut également être défini dynamiquement. En supposant que vous ne connaissiez que le nom de votre nouvelle valeur d'énumération, 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).

N'oubliez pas qu'en JavaScript, un objet est comme une carte ou une table de hachage . Un ensemble de paires nom-valeur. Vous pouvez les parcourir ou autrement les manipuler sans en savoir beaucoup à leur sujet à l'avance.

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 au fait, si vous êtes intéressé par les espaces de noms, vous voudrez peut-être jeter un œil à ma solution pour la gestion simple mais puissante des espaces de noms et des dépendances pour JavaScript: Packages JS

Stijn de Witt
la source
Alors, comment iriez-vous créer simplement une TAILLE si vous n'avez que son nom?
Johanisma
2
@Johanisma: Ce cas d'utilisation n'a pas vraiment de sens pour les énumérations, car l'idée générale est que vous connaissez toutes les valeurs à l'avance. Cependant, rien ne vous empêche d'ajouter des valeurs supplémentaires ultérieurement en Javascript. J'ajouterai un exemple de cela à ma réponse.
Stijn de Witt
2
+1 pour le lien vers votre message avec l'approche des propriétés. Élégant dans la mesure où les déclarations de base sont simples, comme dans l'OP, avec des fonctionnalités supplémentaires lorsque vous le souhaitez.
goodeye
@Stijin, j'ai vraiment aimé votre solution mise à jour. Code publié dans les commentaires sur votre blog et en tant que commentaire ci-dessous. Fondamentalement, à l'aide d'une fonction, effectuez la construction des propriétés à partir d'une liste de hachage existante et éventuellement la figer (mkenum_2 dans ma liste). À votre santé.
Andrew Philips
Il existe également une bibliothèque qui l'implémente, incluant également de belles fonctionnalités de comparaison et de recherche inversée: github.com/adrai/enum
Roman M
83

Conclusion: vous ne pouvez pas.

Vous pouvez le simuler, mais vous n'obtiendrez pas de sécurité de type. En règle générale, cela se fait en créant un simple dictionnaire 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 accidentellement redéfinir votre énumérateur, ou avoir accidentellement des valeurs d'énumérateur en double. Par exemple:

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

Éditer

Qu'en est-il de Object.freeze d'Artur Czajka? Cela ne fonctionnerait-il pas pour vous empêcher de vous installer du lundi au jeudi? - Fry Quad

Absolument, Object.freezeréglerait totalement le problème dont je me plaignais. Je voudrais rappeler à tout le monde que lorsque j'ai écrit ce qui précède, Object.freezen'existait pas vraiment.

Maintenant ... maintenant, cela ouvre des possibilités trè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 qu'il ne corresponde probablement pas à toutes les utilisations valides des énumérations, il va très loin.

Randolpho
la source
103
existe-t-il une sécurité de type en javascript?
Scott Evernden
3
Ne mappez donc pas les valeurs aux propriétés des objets. Utilisez getter pour accéder à l'énumérateur (stocké en tant que propriété, disons, d'un objet "privé"). Une implémentation naïve ressemblerait à -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax
2
@Scott Evernden: point pris. @kangax: le fait est que c'est toujours un hack. Les énumérations n'existent tout simplement pas en Javascript, point final, fin de l'histoire. Même le modèle suggéré par Tim Sylvester est toujours un hack moins qu'idéal.
Randolpho
2
Saupoudrer le code avec des littéraux n'est pas très maintenable, il est donc logique de créer des constantes pour lui. Bien sûr, Javascript n'a pas de constantes non plus. Donc, fondamentalement, c'est juste un moyen d'écrire du code propre. Il ne peut pas être appliqué, mais pas beaucoup en Javascript. Vous pouvez redéfinir des constantes ou des fonctions, ou surtout n'importe quoi. EG: document.getElementById = function () {alert ("Vous êtes foutu. Javascript n'est pas de type sécurisé.");};
Stijn de Witt
3
@Randolpho: Et Object.freeze d'Artur Czajka? Cela ne fonctionnerait-il pas pour vous empêcher de vous installer du lundi au jeudi?
Michael - Où est Clay Shirky le
56

Voici ce que nous voulons tous:

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

Vous pouvez maintenant créer vos énumérations:

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

En faisant cela, les constantes peuvent être accédées 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 sur IE: S ... devrait alors rester avec la version précédente)

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

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');
Andre 'Fi'
la source
J'adore la simplicité de cette réponse!
Marquizzo
@Marquizzo (et OP) J'ai créé une version améliorée basée sur cette réponse: stackoverflow.com/a/60309416/1599699
Andrew
53

Dans la plupart des navigateurs modernes, il existe un type de données primitif de symbole qui peut être utilisé pour créer une énumération. Il assurera la sécurité du 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")});

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
Vitalii Fedorenko
la source
C'est la bonne réponse en théorie. En pratique, la prise en charge du navigateur 2015 est loin d'être suffisante. Pas de production prête de loin.
vbraun
1
Bien que la prise en charge du navigateur ne soit pas encore là, c'est la meilleure réponse car elle est proche de ce à quoi elle Symbolest destinée.
rvighne
2
Cependant, les valeurs enum doivent souvent être sérialisables, et les symboles ne sont pas si pratiques pour sérialiser et désérialiser.
Andy
3
Est-ce juste moi ou Object.freezeseulement pour les personnes qui n'ont pas accepté le fait que "monkeypatch à vos risques et périls" soit le contrat social de JS?
Andy
@Andy oui la sérialisation est ennuyeuse. J'ai fini par faire un explicite toJSONsur la classe contenant pour utiliser cette approche: stackoverflow.com/questions/58499828/…
Ciro Santilli 22 冠状 病 六四 事件 法轮功
30

𝗣𝗹𝗮𝗶𝗻 𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲 𝗡𝗮𝗺𝗲𝘀

Passons directement au problème: la taille du fichier. Toutes les autres réponses répertoriées ici gonflent votre code à l'extrême. Je vous présente que pour les meilleures performances possibles, la lisibilité du code, la gestion de projet à grande échelle, la suggestion de syntaxe dans de nombreux éditeurs de code et la réduction de la taille du code par minification, c'est la bonne façon de faire les énumérations: les variables de notation de soulignement.


wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvvvvwvwvwvv

Variables de notation de soulignement

Comme démontré dans le tableau ci-dessus et l'exemple ci-dessous, voici cinq étapes faciles pour commencer:

  1. Déterminez un nom pour le groupe d'énumération. Pensez à un nom qui peut décrire le but de l'énumération ou au moins les entrées de l'énumération. Par exemple, un groupe d'énumérations représentant des couleurs choisies par l'utilisateur pourrait être mieux nommé COLORCHOICES que COLORS.
  2. Décidez si les énumérations dans le groupe sont mutuellement exclusives ou indépendantes. S'ils s'excluent mutuellement, démarrez chaque nom de variable énuméré par ENUM_. Si indépendant ou côte à côte, utilisez INDEX_.
  3. Pour chaque entrée, créez une nouvelle variable locale dont le nom commence par ENUM_ou INDEX_, puis le nom du groupe, puis un trait de soulignement, puis un nom convivial unique pour la propriété
  4. Ajouter un ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_ou INDEXLEN_(si LEN_ou LENGTH_est de préférence personnelle) variables énumérées à la fin. Vous devez utiliser cette variable dans la mesure du possible dans votre code pour vous assurer que l'ajout d'une entrée supplémentaire à l'énumération et l'incrémentation de cette valeur ne cassera pas votre code.
  5. Donnez chaque variable une valeur dénombrée successive d' une plus que le dernier, à partir de 0. Il y a des commentaires sur cette page que par exemple 0ne doit pas être utilisé comme une valeur énumérée parce que 0 == null, 0 == false, 0 == ""et d' autres JS folie. Je vous soumets que, pour éviter ce problème et booster les performances en même temps, utilisez toujours ===et ne laissez jamais ==apparaître dans votre code sauf avec typeof(ex typeof X == "string"). Pendant toutes mes années d'utilisation ===, je n'ai jamais eu de problème avec l'utilisation de 0 comme valeur d'énumération. Si vous êtes toujours délicat, alors 1pourrait être utilisé comme valeur de départ dans les ENUM_énumérations (mais pas dans les INDEX_énumérations) sans pénalité de performance dans de nombreux cas.
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

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

Voici comment je me souviens quand utiliser INDEX_et quand utiliser ENUM_:

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

Cependant, ENUM_peut, dans certaines circonstances, être approprié comme index, comme lors du comptage des occurrences de chaque élément.

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

Observez que, dans le code ci-dessus, il est vraiment facile d'ajouter un nouveau type d'animal de compagnie: il vous suffira d'ajouter une nouvelle entrée ENUM_PET_RATet de la mettre à jour en ENUMLEN_PETconséquence. Il pourrait être plus difficile et bogué d'ajouter une nouvelle entrée dans d'autres systèmes d'énumération.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwwvwvv

𝗘𝘅𝘁𝗲𝗻𝗱 𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀 𝗪𝗶𝘁𝗵 𝗔𝗱𝗱𝗶𝘁𝗶𝗼𝗻

De plus, cette syntaxe d'énumérations permet une extension de classe claire et concise comme indiqué ci-dessous. Pour étendre une classe, ajoutez un numéro d'incrémentation à l' LEN_entrée de la classe parente. Ensuite, terminez la sous-classe avec sa propre LEN_entrée afin que la sous-classe puisse être étendue à l'avenir.

Diagramme d'extension d'addition

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

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

(Longueur: 2 450 octets)

Certains diront peut-être que c'est moins pratique que d'autres solutions: il évite des tonnes d'espace, il prend beaucoup de temps à écrire et il n'est pas recouvert de syntaxe de sucre. Ces personnes auraient raison de ne pas minimiser leur code. Cependant, aucune personne raisonnable ne laisserait de code non minimisé dans le produit final. Pour cette minification, Closure Compiler est le meilleur que je n'ai pas encore trouvé. L'accès en ligne se trouve ici . Le compilateur de fermeture est capable de prendre toutes ces données d'énumération et de les incorporer, ce qui rend votre Javascript super petit et super rapide. Ainsi, Minify avec Closure Compiler. Observer.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwwvwvv

𝗠𝗶𝗻𝗶𝗳𝘆 𝗪𝗶𝘁𝗵 𝗖𝗹𝗼𝘀𝘂𝗿𝗲 𝗖𝗼𝗺𝗽𝗶𝗹𝗲𝗿

Le compilateur de fermeture est capable d'effectuer des optimisations assez incroyables via des inférences qui dépassent largement les capacités de tout autre minifieur Javascript. Le compilateur de fermeture peut incorporer des variables primitives définies à une valeur fixe. Closure Compiler est également capable de faire des inférences sur la base de ces valeurs intégrées et d'éliminer les blocs inutilisés dans les instructions if et les boucles.

Wringing code via Closure Compiler

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

(Longueur: 605 octets)

Closure Compiler vous récompense pour un codage plus intelligent et une bonne organisation de votre code car, alors que de nombreux minificateurs punissent le code organisé avec une taille de fichier minifiée plus grande, Closure Compiler est capable de passer au crible toute votre propreté et votre santé mentale pour produire une taille de fichier encore plus petite si vous utilisez des astuces comme les énumérations de nom de variable. Dans cet esprit, c'est le Saint Graal du codage: un outil qui aide à la fois votre code avec une taille réduite et aide votre esprit en formant de meilleures habitudes de programmation.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwwvwvv

𝗦𝗺𝗮𝗹𝗹𝗲𝗿 𝗖𝗼𝗱𝗲 𝗦𝗶𝘇𝗲

Voyons maintenant la taille du fichier équivalent sans aucune de ces énumérations.

Source sans utiliser d'énumérations (longueur: 1 973 octets (477 octets de moins que le code énuméré!))
Minifié sans utiliser d'énumérations (longueur: 843 octets (238 octets de plus que le code énuméré ))

Tableau des tailles de code



Comme vu, sans énumérations, le code source est plus court au prix d'un code minifié plus grand. Je ne te connais pas; mais je sais avec certitude que je n'incorpore pas de code source dans le produit final. Ainsi, cette forme d'énumération est bien supérieure dans la mesure où elle se traduit par des tailles de fichier minifiées plus petites.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwwvwvv

𝗖𝗼𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝘃𝗲 🤝 𝗕𝘂𝗴 𝗙𝗶𝘅𝗶𝗻𝗴

Un autre avantage de cette forme d'énumération est qu'elle peut être utilisée pour gérer facilement des projets à grande échelle sans sacrifier la taille du code minifié. Lorsque vous travaillez sur un grand projet avec de nombreuses autres personnes, il peut être avantageux de marquer et d'étiqueter explicitement les noms de variable avec qui a créé le code afin que le créateur d'origine du code puisse être rapidement identifié pour la correction collaborative de bogues.

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

// later on

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

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

// later on

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

𝗦𝘂𝗽𝗲𝗿𝗶𝗼𝗿 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

De plus, cette forme de dénombrement est également beaucoup plus rapide après minification. Dans les propriétés nommées normales, le navigateur doit utiliser des hashmaps pour rechercher où se trouve la propriété sur l'objet. Bien que les compilateurs JIT mettent en cache intelligemment cet emplacement sur l'objet, il y a encore une énorme surcharge due à des cas spéciaux tels que la suppression d'une propriété inférieure de l'objet.

Mais, avec les tableaux PACKED_ELEMENTS indexés en nombre entier non épars continus , le navigateur peut ignorer une grande partie de cette surcharge car l'index de la valeur dans le tableau interne est déjà spécifié. Oui, selon la norme ECMAScript, toutes les propriétés sont censées être traitées comme des chaînes. Néanmoins, cet aspect de la norme ECMAScript est très trompeur sur les performances car tous les navigateurs ont des optimisations spéciales pour les index numériques dans les tableaux.

/// Hashmaps are slow, even with JIT juice
var ref = {};
ref.count = 10;
ref.value = "foobar";

Comparez le code ci-dessus au code ci-dessous.

/// Arrays, however, are always lightning fast
const INDEX_REFERENCE_COUNT = 0;
const INDEX_REFERENCE_VALUE = 1;
const INDEXLENGTH_REFERENCE = 2;

var ref = [];
ref[INDEX_REFERENCE_COUNT] = 10;
ref[INDEX_REFERENCE_VALUE] = "foobar";

On pourrait s'opposer au code avec des énumérations qui semblent être beaucoup plus longues que le code avec des objets ordinaires, mais les apparences peuvent être trompeuses. Il est important de se rappeler que la taille du code source n'est pas proportionnelle à la taille de sortie lors de l'utilisation du compilateur de fermeture épique. Observer.

/// Hashmaps are slow, even with JIT juice
var a={count:10,value:"foobar"};

Le code minifié sans énumérations est au-dessus et le code minifié avec énumérations est ci-dessous.

/// Arrays, however, are always lightning fast
var a=[10,"foobar"];

L'exemple ci-dessus montre qu'en plus d'avoir des performances supérieures, le code énuméré entraîne également une taille de fichier réduite plus petite.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwwvwvv

𝗘𝗮𝘀𝘆 𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

De plus, la cerise sur le gâteau de celui-ci utilise cette forme d'énumération avec l' éditeur de texte CodeMirror en mode Javascript. Le mode de mise en évidence de la syntaxe Javascript de CodeMirror met en évidence les variables locales dans la portée actuelle. De cette façon, vous savez instantanément quand vous tapez correctement un nom de variable car si le nom de variable a été précédemment déclaré avec le varmot - clé, alors le nom de variable prend une couleur spéciale (cyan par défaut). Même si vous n'utilisez pas CodeMirror, au moins le navigateur lance un utile[variable name] is not definedexception lors de l'exécution de code avec des noms d'énumération mal tapés. En outre, les outils JavaScript tels que JSLint et Closure Compiler sont très bruyants pour vous dire quand vous saisissez mal le nom d'une variable d'énumération. CodeMirror, le navigateur et divers outils Javascript réunis rendent le débogage de cette forme d'énumération très simple et très facile.

Démonstration de mise en évidence de CodeMirror

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

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

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

Dans l'extrait ci-dessus, vous avez été alerté d'une erreur car il ENUM_COLORENUM_DNEn'existe pas.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwwvwvv

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Je pense qu'il est prudent de dire que cette méthodologie d'énumération est en effet la meilleure voie à suivre non seulement pour la taille de code minifiée, mais aussi pour les performances, le débogage et la collaboration.

Après avoir lu une question utile, je remercie l'auteur d'avoir consacré du temps à son écriture en cliquant sur la flèche vers le haut en haut à gauche dans la zone de question. Chaque boîte de réponse a également l'une de ces flèches vers le haut.

Jack Giffin
la source
Eh. Je préfère fortement la lisibilité et la facilité d'utilisation et de compréhension à la taille du code.
Andrew
1
@Andrew Avec ma réponse, vous pouvez avoir les deux. Ma réponse se traduit par le code le plus facile à utiliser / gérer et par la plus petite taille de code minifiée.
Jack Giffin
1
@Andrew J'ai tenté d'appliquer votre Yet Another Enum (YEA!) À l'exemple d'analyseur de couleurs dans ma réponse. Cependant, j'ai trouvé plusieurs problèmes que vous souhaiterez peut-être résoudre. YEA n'a aucun moyen d'étendre les énumérations avec des sous-classes, m'obligeant à créer des classes parent et enfant séparées, ce qui pourrait être assez difficile à gérer sur de grands projets. YEA ne garantit pas que l'entrée existe (ex colors.REEDrendements undefined), donc les fautes de frappe créent des énigmes insaisissables. YEA ne fait pas de distinction entre l'utilisation des énumérations en tant qu'index et ID, ce qui entraîne une confusion dans le code où tout se ressemble. …
Jack Giffin
1
@Andrew… YEA entrave la capacité de Closure Compiler à se réduire. Comparez le code source avec YEA (3549 octets) au code minifié avec YEA (1344 octets) au code minifié avec ma solution (604 octets). Enfin, YEA implique le «mappage par nom» car il sépare les noms de chaîne des ID énumérés. Le mien ne considère que l'ID, donc aucun "mappage par nom" n'est nécessaire, ce qui conduit à une conception plus simple et à de meilleures performances. Merci de partager votre solution, mais elle a besoin de nombreuses corrections avant d'être pratique.
Jack Giffin
1
@Andrew Vous avez droit à votre opinion comme je suis à la mienne 👍
Jack Giffin
23

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

En utilisant Object.definePropertyje pense avoir trouvé une solution quelque peu viable.

Voici un jsfiddle: http://jsfiddle.net/ZV4A6/

En utilisant cette méthode .. vous devriez (en théorie) être capable d'appeler et de définir des valeurs d'énumération 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:falsecela devrait le rendre sûr.

Vous devriez donc pouvoir créer un objet personnalisé, puis faire appel Enum()à lui. 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
Duncan
la source
3
Si vous ajoutez return this;à la fin d'Enum, vous pourriez faire:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP
Je n'y ai pas pensé, car ce n'est pas ma méthode normale de faire les choses. Mais vous avez absolument raison! Je vais le modifier.
Duncan
J'aime vraiment ça bien que je ne sois pas un grand fan du nettoyage de l'espace objet (avec la fonction globale ENUM). Converti cela en une fonction mkenum et ajouté des affectations numériques facultatives => var mixedUp = mkenum ('NOIR', {ROUGE: 0x0F00, BLEU: 0X0F, VERT: 0x0F0, BLANC: 0x0FFF, UN: 1}, DEUX, TROIS, QUATRE) ; // Ajout de mon code comme réponse ci-dessous. Merci.
Andrew Philips
Pour être honnête, je ne l'utilise même plus. J'ai utilisé le compilateur de fermeture de Google, et cela ne fonctionne pas trop bien (ou cela complique simplement les choses) si vous utilisez le paramètre avancé. Je viens donc de revenir à la notation d'objet standard.
Duncan
1
falseest la valeur par défaut pour writable, enumerableet configurable. Pas besoin de mâcher les défauts.
ceving
23

Utiliser des proxys Javascript

TLDR: ajoutez cette classe à vos méthodes utilitaires et utilisez-la dans tout votre code, elle se moque du comportement Enum des langages de programmation traditionnels et génère en fait des erreurs lorsque vous essayez d'accéder à un énumérateur qui n'existe pas ou d'ajouter / mettre à jour un énumérateur. Pas besoin de compter 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);
  }
}

Créez ensuite 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 langues traditionnelles est qu'elles explosent (provoquent une erreur de compilation) si vous essayez d'accéder à un énumérateur qui n'existe pas.

Outre le gel de la structure d'énumération simulée pour empêcher l'ajout accidentel / malveillant de valeurs supplémentaires, aucune des autres réponses ne traite de cette caractéristique intrinsèque d'énumérations.

Comme vous le savez probablement, l'accès aux membres non existants en JavaScript revient simplement undefinedet ne fait pas exploser votre code. Étant donné que les énumérateurs sont 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 de retour undefinedlors 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 les objets proxy brillent. 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 (par exemple, recherche de propriété, affectation, énumération, appel de fonction, etc.).

Semblables à un proxy de serveur Web, les proxys JavaScript sont capables d'intercepter des opérations sur des objets (avec l'utilisation de "pièges", appelez-les hooks si vous le souhaitez) et vous permettent d'effectuer diverses vérifications, actions et / ou manipulations avant qu'elles ne se terminent (ou 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 les énumérations. Les énumérateurs dans cet exemple sont des méthodes HTTP standard (c'est-à-dire "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"


HORS: Qu'est-ce que c'est qu'un proxy?

Je me souviens quand j'ai commencé à voir le mot proxy partout, cela n'avait définitivement aucun sens pour moi pendant longtemps. Si c'est vous en ce moment, je pense qu'un moyen facile de généraliser les procurations est de les considérer comme des logiciels, des institutions ou même des personnes qui agissent comme intermédiaires ou intermédiaires entre deux serveurs, sociétés ou personnes.

Govind Rai
la source
Comment faire quelque chose comme myEnum.valueOf ("someStringValue")? Attendu: dans le cas où la chaîne d'entrée a une valeur d'un élément de l'énumérateur, devrait retourner l'élément. Dans le cas où aucun élément n'a cette valeur de chaîne, lever l'exception.
sscarduzio
@sscarduzio vous pouvez remplacer la valueOfméthode par défaut en la spécifiant comme méthode d'instance sur la classe Enum. Cependant, pourquoi voulez-vous y accéder de cette façon par rapport à un simple accès par notation par points?
Govind Rai, du
Mon enum est const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}) et j'analyse en entrée une chaîne arbitraire "info" ou "debug". J'ai donc besoin de quelque chose comme currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio
1
Pourquoi ne pourriez-vous pas simplement le faire logLevelEnum[settings.get("log_level")]? l'ajout parseOrThrowserait simplement répétitif à ce que les pièges proxy font déjà pour vous.
Govind Rai
17

C'est un ancien que je connais, mais la façon dont il a depuis é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 celui MyEnum.Barqui renvoie 1 et celui MyEnum[1]qui renvoie "Bar" quel que soit l'ordre de déclaration.

Rob Hardy
la source
1
De plus, MyEnum ["Bar"] fonctionne qui renvoie 1 ... <3 TypeScript jusqu'à présent ...
David Karlaš
3
et bien sûr si vous utilisez réellement Typographie:enum MyEnum { Foo, Bar, Foobar }
parlement
16

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 une classe au lieu d'un objet littéral) est d'avoir une classe parente Enum alors tous vos énumérations sera étend cette classe.

 class ColorEnum  extends Enum {/*....*/}
Abdennour TOUMI
la source
4
Pourriez-vous expliquer pourquoi avoir une classe de parents est un avantage, s'il vous plaît? J'ai l'impression de manquer quelque chose!
Jon G
7
Ne fais pas ça. new ColorEnum()n'a absolument aucun sens.
Bergi
3
étendre une énumération semble fou, vraiment
Codii
une fois que le langage ne le prend pas en charge nativement, il serait logique de conserver cette convention et de l'utiliser comme ça! je suis d'accord!
jusqu'au
Je pense que (?) Ce que OP veut dire, c'est: L'avantage de la statique pure est qu'elle est disponible partout en tant que singleton, et vous n'avez pas besoin d'instancier la classe - OP ne suggère pas que vous le fassiez! Je pense que ce qu'il dit est que la superclasse Enuma norme statique méthodes de recenseur sur elle, comme getValues(), getNames(), iterate(), etc. Si tel est le cas, vous ne devez pas les réimplémenter pour chaque nouveau genre enum.
Ingénieur
15

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 de cette approche:

  • Facile à déclarer les énumérations
  • Accédez facilement à vos énumérations
  • Vos énumérations peuvent être de types complexes
  • La classe Enum a une mise en cache associative si vous utilisez beaucoup getByValue

Quelques inconvénients:

  • Une gestion de la mémoire en désordre qui se passe là-bas, car je garde les références aux énumérations
  • Toujours pas de sécurité de type
Chris
la source
14

Créez un objet littéral:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
hvdd
la source
12
constne rend pas les propriétés de l'objet immuables, cela signifie seulement que la variable Modesne peut pas être réaffectée à autre chose. Pour le rendre plus complet, utilisez-le à Object.freeze()côté const.
rvighne
Veuillez ne pas utiliser Object.freeze. Il empêche le compilateur de fermeture d'insérer l'objet.
Jack Giffin
11

Si vous utilisez Backbone , vous pouvez obtenir gratuitement des fonctionnalités d'énumération complètes (rechercher par identifiant, 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()
Iaroslav
la source
8

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
Xeltor
la source
1
@JackGiffin Je suis d'accord que votre réponse est plus performante et que la mienne peut prendre plus de mémoire, bien que vous ne devriez pas supposer que tout le monde veut une énumération comme C ++ l'a implémentée. Veuillez respecter les autres réponses et les développeurs qui pourraient préférer celle-ci aux vôtres.
Xeltor
7

J'ai modifié la solution d'André '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
David Miró
la source
6

J'ai proposé cette approche qui s'inspire des énumérations en Java. Celles-ci sont sécurisées et vous pouvez donc également effectuer des instanceofvérifications.

Vous pouvez définir des énumérations comme ceci:

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

Daysfait maintenant référence à l' Daysénumération:

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
    }

})();
Vivin Paliath
la source
Cela a l'air bien, peut-être devriez-vous vérifier l'existence de la freezeméthode de compatibilité descendante? Par exemple,if (Object.freeze) { Object.freeze(values); }
FBB
Bon point! Ça ira!
Vivin Paliath
6

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 des colonnes de lignes.

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

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

On pourrait 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 semble le plus efficace pour la chaîne, il réduit votre bande passante totale si vous avez un serveur et un client échangeant ces données.
Bien sûr, il est maintenant de votre devoir de vous assurer qu'il n'y a pas de conflits dans les données (RE, EX, etc. doivent être uniques, également 1, 2, etc. doivent être uniques). Notez que vous devez les conserver indéfiniment pour une compatibilité descendante.

Affectation:

var wildType = CONST_WILD_TYPES.REGULAR;

Comparaison:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}
Manohar Reddy Poreddy
la source
5
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

Vous n'avez pas besoin de vous assurer que vous n'attribuez pas de numéros en double à différentes valeurs d'énumération de cette façon. Un nouvel objet est instancié et affecté à toutes les valeurs d'énumération.

Shivanshu Goyal
la source
Cette réponse est sous-estimée. C'est l'une de mes idées préférées pour sa simplicité. En pratique, je pense que je m'en tiendrai aux chaînes car il est plus facile de déboguer pour l'instant.
Domino
Hmm, assurez-vous simplement que ce code ne soit pas appelé deux fois ...
Andrew
4

Voici quelques façons d'implémenter les énumérations TypeScript .

Le moyen le plus simple consiste à simplement itérer 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 mixin lodash pour créer une énumération à l'aide d'une chaîne. Bien que cette version soit un peu plus impliquée, elle effectue automatiquement la numérotation pour vous. Toutes les méthodes lodash utilisées dans cet exemple ont un équivalent JavaScript normal, vous pouvez donc facilement les désactiver 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
Blake Bowen
la source
très intelligent, merci
Ilan
4

Je viens de publier un package NPM gen_enum vous permet de créer rapidement une structure de données Enum en Javascript:

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 

Une chose intéressante à propos de ce petit outil est dans un environnement moderne (y compris les navigateurs nodejs et IE 9+) l'objet Enum retourné est immuable.

Pour plus d'informations, veuillez consulter https://github.com/greenlaw110/enumjs

Mises à jour

J'ai un gen_enumpackage obsolète et je fusionne la fonction dans le package constjs , qui fournit plus de fonctionnalités, y compris les objets immuables, la désérialisation des chaînes JSON, les constantes de chaîne et la génération de bitmap, etc. Consultez https://www.npmjs.com/package/constjs pour plus d'informations.

Pour passer de gen_enumà constjssimplement changer la déclaration

var genEnum = require('gen_enum');

à

var genEnum = require('constjs').enum;
Gelin Luo
la source
4

Solution la plus simple:

Créer

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

Obtenez de la valeur

console.log(Status.Ready) // 1

Obtenir la clé

console.log(Object.keys(Status)[Status.Ready]) // Ready
Ilya Gazman
la source
4

J'ai créé 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 l'initier 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 des noms différents):

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 facilement les options de sélection html:

var html = enum1.GetSelectOptionsHTML();

Qui tient:

"<option value='0'>item1</option>..."
Oooogi
la source
4

Même si seules les méthodes statiques (et non les propriétés statiques) sont prises en charge dans ES2015 (voir ici également, §15.2.2.2), vous pouvez curieusement utiliser ce qui suit avec Babel avec le es2015préréglage:

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 trouvé que cela fonctionnait comme prévu, même entre les modules (par exemple, l'importation de l' CellStateénumération à partir d'un autre module) et également lorsque j'importe un module à l'aide de Webpack.

L'avantage de cette méthode sur la plupart des autres réponses est que vous pouvez l'utiliser avec un vérificateur de type statique (par exemple Flow ) et que vous pouvez affirmer, au moment du développement à l'aide de la vérification de type statique, que vos variables, paramètres, etc. sont du type CellState" enum "plutôt que d'autres énumérations (qui seraient impossibles à distinguer si vous utilisiez des objets ou des symboles génériques).

mise à jour

Le code ci-dessus a une déficience 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 CellStatecar il est gelé). Pourtant, le code ci-dessous plus raffiné offre les avantages suivants:

  1. aucun autre objet de type ne CellStatepeut être créé
  2. vous avez la garantie que deux instances d'énumération ne reçoivent pas le même code
  3. méthode utilitaire pour récupérer l'énumération à partir d'une représentation sous forme de chaîne
  4. la valuesfonction 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;
Marcus Junius Brutus
la source
Bon exemple :-)
Ashraf.Shk786
4

manière es7, (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(', ') }

}
Joseph Merdrignac
la source
4

Voici comment Typescript le traduit enumen Javascript:

var makeEnum = function(obj) {
    obj[ obj['Active'] = 1 ] = 'Active';
    obj[ obj['Closed'] = 2 ] = 'Closed';
    obj[ obj['Deleted'] = 3 ] = 'Deleted';
}

Maintenant:

makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}

Au début, je ne savais pas pourquoi obj[1]revenir 'Active', mais je me suis ensuite rendu compte que son simple mort - L'opérateur d'affectation attribue une valeur, puis la renvoie:

obj['foo'] = 1
// => 1
Julius Dzidzevičius
la source
4

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

LNT
la source
3

Un moyen rapide et simple serait:

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

console.log(Colors.WHITE)  //this prints out "0"
user2254487
la source
6
La fonction est inutile et vous donne exactement le même résultat que ce que l'OP a publié.
Sildoreth
3

Au moment de la rédaction, octobre 2014 - voici donc une solution contemporaine. J'écris la solution en tant que module de nœud et j'ai inclus un test utilisant Mocha et Chai, ainsi que underscoreJS. Vous pouvez facilement les ignorer et prendre le code Enum si vous préférez.

Vu beaucoup de messages 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 qu'il vous apporte:

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 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 l'usine avec différentes valeurs et exporter les énumérations générées en utilisant l'approche modulaire de Node.

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

arcseldon
la source
5
Pourriez-vous poster une réponse avec simplement "voici comment faire cela en tant qu'utilisateur occasionnel qui veut juste des énumérations, pas des usines, des traits de soulignement ou quelque chose de fantaisiste"?
GreenAsJade
5
Même si c'est assez génial du point de vue d'un développeur, ce n'est pas très propre ou lisible. La solution Enum de l'OP est plus facile et plus lisible dans tous les sens, et donc meilleure à utiliser. Pourtant, assez génial que vous ayez trouvé ça.
David
3

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

MISE À JOUR:

Il y a mes codes d'assistance ( TypeHelper).

Sherali Turdiyev
la source