Was ist die bevorzugte Syntax zum Definieren von Aufzählungen in JavaScript?

2082

Was ist die bevorzugte Syntax zum Definieren von Aufzählungen in JavaScript? Etwas wie:

my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
   // whatever
}

Oder gibt es eine vorzuziehende Sprache?

David Citron
quelle
131
Nicht 0als Aufzählungsnummer verwenden. Es sei denn, es wird für etwas verwendet, das nicht eingestellt wurde. JS behandelt false || undefined || null || 0 || "" || '' || NaNalle im Vergleich mit ==.
Matsko
152
@matsko ist das nicht nur ein Argument gegen die Verwendung von ==?
SDM350
6
0 == nullgibt false zurück
mcont
11
Aber false == 0und +null == 0(und Umrechnungen in Zahlen passieren manchmal, wenn Sie es nicht erwarten), während null == undefined, und +undefinedist NaN(obwohl NaN != NaN).
Sanderd17
46
Die Doppelgleichheitsmatrix ist verwirrender als die automatische Formatierung von Microsoft Word
aaaaaa

Antworten:

896

Seit 1.8.5 ist es möglich, das Objekt zu versiegeln und einzufrieren. Definieren Sie Folgendes wie folgt:

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

oder

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

und voila! JS-Aufzählungen.

Dies hindert Sie jedoch nicht daran, einer Variablen einen unerwünschten Wert zuzuweisen, was häufig das Hauptziel von Aufzählungen ist:

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

Eine Möglichkeit, ein höheres Maß an Typensicherheit (mit Aufzählungen oder auf andere Weise) zu gewährleisten, ist die Verwendung eines Tools wie TypeScript oder Flow .

Quelle

Zitate werden nicht benötigt, aber ich habe sie aus Gründen der Konsistenz aufbewahrt.

Artur Czajka
quelle
6
Laut Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ) gilt dies für Firefox 4, IE 9, Opera 11.60 und ich weiß, dass es in Chrome funktioniert.
Artur Czajka
77
Dies ist jetzt 2012 die richtige Antwort. Einfacher : var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Sie müssen keine ID angeben, sondern können nur ein leeres Objekt zum Vergleichen von Aufzählungen verwenden. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas
34
Aus if (Object.freeze) { Object.freeze(DaysEnum); }
Gründen der
17
Ich möchte darauf hinweisen, dass das Tun ({ monday: {}, usw. bedeutet, dass Sie erhalten, wenn Sie dieses Objekt über stringify in JSON konvertieren, [{"day": {}}]was nicht funktioniert.
JCollum
10
@Supuhstar Meine Meinung zu dieser Frage ist jetzt anders. Verwenden Sie nicht freeze (), es ist völlig nutzlos und Zeitverschwendung, "dumme" Dinge zu tun. Wenn Sie eine Aufzählung verfügbar machen möchten, legen Sie einfach Folgendes offen : var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Das Vergleichen von Objekten wie in meinem vorherigen Kommentar ist VIEL langsamer als das Vergleichen von Zahlen.
Gabriel Llamas
608

Dies ist keine gute Antwort, aber ich würde sagen, dass das persönlich gut funktioniert

Da es jedoch keine Rolle spielt, welche Werte verwendet werden (Sie haben 0, 1, 2 verwendet), würde ich eine aussagekräftige Zeichenfolge verwenden, falls Sie jemals den aktuellen Wert ausgeben möchten.

Gareth
quelle
377
Dies wurde in einer anderen Antwort angegeben, aber da diese Antwort die akzeptierte Antwort ist, werde ich dies hier posten. Die Lösung des OP ist korrekt. Es wird jedoch noch besser sein, wenn es mit verwendet wird Object.freeze(). Dadurch wird verhindert, dass anderer Code die Werte der Aufzählung ändert. Beispiel:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth
5
@ TolgaE danke für diese Bibliothek! Es hat mich inspiriert, es nicht nur auf das Nötigste zu reduzieren, sondern auch ein paar Features hinzuzufügen! Ich habe deine gegabelt und alles hier
abgelegt
3
@Supuhstar Das ist großartig! Ich bin froh, dass Sie es verwenden konnten. Sie können gerne eine Pull-Anfrage stellen, wenn Sie möchten, dass es in dieser Bibliothek zusammengeführt wird. Dann kann ich die npm-Bibliothek aktualisieren
Tolga E
2
Wenn jemand interessiert ist, habe ich umgesetzt typsichere Aufzählungen ähnlich, wie sie in Java. Dies bedeutet, dass Sie instanceofÜberprüfungen durchführen können. Zum Beispiel ColorEnum.RED instanceof ColorEnum(kehrt zurück true). Sie können eine Instanz auch aus einem Namen heraus auflösen ColorEnum.fromName("RED") === ColorEnum.RED(Rückgabe true). Jede Instanz hat auch eine .name()und eine .ordinal()Methode, und die Aufzählung selbst hat eine values()Methode, die ein Array aller Konstanten zurückgibt.
Vivin Paliath
3
Ich bin mir nicht sicher, ob ich dem Vorschlag "Sinnvolle Zeichenfolge" zustimme. Aufzählungen sollten nicht als Zeichenfolgen oder Zahlen betrachtet werden. Sie sind abstrakte Datentypen. Es sollte nicht möglich sein, den aktuellen Wert ohne eine Hilfsmethode auszugeben. In Java und .NET ist es die ToString()Methode. Wir JS-Entwickler sind schon viel zu sehr auf Dinge angewiesen, die "nur funktionieren"! Auch sollte man in der Lage sein, schnell switchauf eine Aufzählung zuzugreifen. Das Vergleichen von Zeichenfolgen ist langsamer als das Vergleichen switchvon Zahlen. Wenn Sie also Zeichenfolgen anstelle von Ganzzahlen verwenden , erhalten Sie eine etwas schlechtere Leistung.
Rabadash8820
501

AKTUALISIEREN

Vielen Dank für all die positiven Stimmen, aber ich denke nicht, dass meine Antwort unten der beste Weg ist, Enums in JavaScript zu schreiben. Weitere Informationen finden Sie in meinem Blogbeitrag: Aufzählungen in JavaScript .


Das Benachrichtigen des Namens ist bereits möglich:

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

Alternativ können Sie die Werteobjekte erstellen, damit Sie den Kuchen auch essen können:

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

In JavaScript ist es als dynamische Sprache sogar möglich, dem Satz später Aufzählungswerte hinzuzufügen:

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

Denken Sie daran, dass die Felder der Aufzählung (Wert, Name und Code in diesem Beispiel) für die Identitätsprüfung nicht benötigt werden und nur zur Vereinfachung dienen. Auch der Name der size-Eigenschaft selbst muss nicht fest codiert sein, sondern kann auch dynamisch festgelegt werden. Angenommen, Sie kennen nur den Namen für Ihren neuen Aufzählungswert, können Sie ihn dennoch problemlos hinzufügen:

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

Dies bedeutet natürlich, dass einige Annahmen nicht mehr getroffen werden können (dieser Wert repräsentiert beispielsweise die richtige Reihenfolge für die Größe).

Denken Sie daran, dass ein Objekt in JavaScript wie eine Karte oder eine Hash-Tabelle ist . Eine Reihe von Name-Wert-Paaren. Sie können sie durchlaufen oder auf andere Weise manipulieren, ohne vorher viel über sie zu wissen.

Beispiel

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

Übrigens, wenn Sie an Namespaces interessiert sind, möchten Sie vielleicht einen Blick auf meine Lösung für einfaches, aber leistungsstarkes Namespace- und Abhängigkeitsmanagement für JavaScript werfen: Packages JS

Stijn de Witt
quelle
Wie würden Sie dann einfach eine GRÖSSE erstellen, wenn Sie nur ihren Namen haben?
Johanisma
2
@Johanisma: Dieser Anwendungsfall ist für Aufzählungen nicht wirklich sinnvoll, da die ganze Idee davon ist, dass Sie alle Werte im Voraus kennen. Nichts hindert Sie jedoch daran, später in Javascript zusätzliche Werte hinzuzufügen. Ich werde meiner Antwort ein Beispiel dafür hinzufügen.
Stijn de Witt
2
+1 für den Link zu Ihrem Beitrag mit dem Eigenschaftenansatz. Elegant, da die grundlegenden Deklarationen wie im OP einfach sind und auf Wunsch zusätzliche Eigenschaften aufweisen.
Goodeye
@Stijin, hat Ihre aktualisierte Lösung wirklich gut gefallen. Geposteter Code in Kommentaren in Ihrem Blog und als Kommentar unten. Führen Sie mithilfe einer Funktion die Eigenschaften aus, die aus einer vorhandenen Hash-Liste erstellt wurden, und frieren Sie sie optional ein (mkenum_2 in meiner Liste). Prost.
Andrew Philips
Es gibt auch eine Bibliothek, die es implementiert, einschließlich netter Funktionen zum Vergleich und zur umgekehrten Suche: github.com/adrai/enum
Roman M
83

Fazit: Das geht nicht.

Sie können es fälschen, aber Sie erhalten keine Typensicherheit. In der Regel wird dazu ein einfaches Wörterbuch mit Zeichenfolgenwerten erstellt, die ganzzahligen Werten zugeordnet sind. Zum Beispiel:

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}

Document.Write("Enumerant: " + DaysEnum.tuesday);

Das Problem mit diesem Ansatz? Sie können Ihren Enumeranten versehentlich neu definieren oder versehentlich doppelte Enumerantenwerte haben. Zum Beispiel:

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

Bearbeiten

Was ist mit Artur Czajkas Object.freeze? Würde das nicht funktionieren, um Sie daran zu hindern, Montag auf Donnerstag zu setzen? - Braten Sie Quad

Absolut, Object.freezeWürde das Problem, über das ich mich beschwert habe absolut beheben. Ich möchte alle daran erinnern, dass es das, als ich das oben Genannte schrieb, Object.freezenicht wirklich gab.

Jetzt ... jetzt öffnet es einige sehr interessante Möglichkeiten.

Bearbeiten 2
Hier ist eine sehr gute Bibliothek zum Erstellen von Aufzählungen.

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

Während es wahrscheinlich nicht zu jeder gültigen Verwendung von Aufzählungen passt, geht es einen sehr langen Weg.

Randolpho
quelle
103
Gibt es Typensicherheit in Javascript?
Scott Evernden
3
Ordnen Sie also keine Werte Objekteigenschaften zu. Verwenden Sie getter, um auf enumerant zuzugreifen (gespeichert als Eigenschaft von beispielsweise "privatem" Objekt). Eine naive Implementierung würde aussehen wie -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
Kangax
2
@ Scott Evernden: Punkt genommen. @ Kangax: Der Punkt ist, dass es immer noch ein Hack ist. Aufzählungen existieren einfach nicht in Javascript, Punkt, Ende der Geschichte. Sogar das von Tim Sylvester vorgeschlagene Muster ist immer noch ein weniger als idealer Hack.
Randolpho
2
Das Besprühen des Codes mit Literalen ist nicht sehr wartbar, daher ist es sinnvoll, Konstanten dafür zu erstellen. Natürlich hat Javascript auch keine Konstanten. Im Grunde ist dies nur eine Möglichkeit, sauberen Code zu schreiben. Es kann nicht erzwungen werden, aber nicht viel in Javascript kann. Sie können Konstanten oder Funktionen oder meistens alles neu definieren. EG: document.getElementById = function () {alert ("Sie sind geschraubt. Javascript ist nicht typsicher.");};
Stijn de Witt
3
@ Randolpho: Was ist mit Artur Czajkas Object.freeze? Würde das nicht funktionieren, um Sie daran zu hindern, Montag auf Donnerstag zu setzen?
Michael - Wo ist Clay Shirky
56

Folgendes wollen wir alle:

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

Jetzt können Sie Ihre Aufzählungen erstellen:

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

Auf diese Weise können Konstanten wie gewohnt aufgerufen werden (YesNo.YES, Color.GREEN) und sie erhalten einen sequentiellen int-Wert (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).

Sie können auch Methoden hinzufügen, indem Sie Enum.prototype verwenden:

Enum.prototype.values = function() {
    return this.allValues;
    /* for the above to work, you'd need to do
            this.allValues = constantsList at the constructor */
};


Bearbeiten - kleine Verbesserung - jetzt mit varargs: (leider funktioniert es nicht richtig auf IE: S ... sollte dann bei der vorherigen Version bleiben)

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'
quelle
Lieben Sie die Einfachheit dieser Antwort!
Marquizzo
@Marquizzo (und OP) Ich habe eine verbesserte Version basierend auf dieser Antwort erstellt: stackoverflow.com/a/60309416/1599699
Andrew
53

In den meisten modernen Browsern gibt es einen primitiven Symboldatentyp , mit dem eine Aufzählung erstellt werden kann. Dies gewährleistet die Typensicherheit der Aufzählung, da jeder Symbolwert durch JavaScript als eindeutig garantiert wird, d Symbol() != Symbol(). H. Zum Beispiel:

const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});

Um das Debuggen zu vereinfachen, können Sie den Aufzählungswerten eine Beschreibung hinzufügen:

const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});

Plunker-Demo

Auf GitHub finden Sie einen Wrapper, der den Code vereinfacht, der zum Initialisieren der Enumeration erforderlich ist:

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
quelle
Dies ist theoretisch die richtige Antwort. In der Praxis ist die Browserunterstützung für 2015 bei weitem nicht ausreichend. Bei weitem nicht produktionsbereit.
Vbraun
1
Obwohl die Browserunterstützung noch nicht vorhanden ist, ist dies die beste Antwort, da dies in etwa dem entspricht, wofür Symboles vorgesehen ist.
Rvighne
2
Meh ... enum-Werte müssen jedoch häufig serialisierbar sein, und Symbole sind nicht so praktisch zum Serialisieren und Deserialisieren.
Andy
3
Ist es nur ich oder Object.freezenur für Leute, die nicht akzeptiert haben, dass "Monkeypatch auf eigenes Risiko" der Gesellschaftsvertrag von JS ist?
Andy
@ Andy ja Serialisierung ist nervig. toJSONAm Ende habe ich eine explizite Beschreibung
Ciro Santilli 22 病毒 审查 六四 事件 22
30

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

Kommen wir gleich zum Problem: Dateigröße. Jede andere hier aufgeführte Antwort bringt Ihren Code auf das Äußerste. Ich präsentiere Ihnen, dass dies für die bestmögliche Leistung, Lesbarkeit des Codes, umfangreiches Projektmanagement, Syntaxhinweise in vielen Code-Editoren und Reduzierung der Codegröße durch Minimierung die richtige Methode ist, um Aufzählungen durchzuführen: Unterstrich-Notationsvariablen.


wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

Unterstrich-Notationsvariablen

Wie in der obigen Tabelle und im folgenden Beispiel gezeigt, sind hier fünf einfache Schritte, um loszulegen:

  1. Bestimmen Sie einen Namen für die Aufzählungsgruppe. Stellen Sie sich ein Substantiv vor, das den Zweck der Aufzählung oder zumindest die Einträge in der Aufzählung beschreiben kann. Beispielsweise kann eine Gruppe von Aufzählungen, die vom Benutzer auswählbare Farben darstellen, besser als COLORCHOICES als COLORS bezeichnet werden.
  2. Entscheiden Sie, ob sich Aufzählungen in der Gruppe gegenseitig ausschließen oder unabhängig sind. Wenn sich gegenseitig ausschließen, starten Sie jeden aufgezählten Variablennamen mit ENUM_. Wenn unabhängig oder nebeneinander, verwenden Sie INDEX_.
  3. Erstellen Sie für jeden Eintrag eine neue lokale Variable, deren Name mit ENUM_oder beginnt INDEX_, dann den Namen der Gruppe, dann einen Unterstrich und dann einen eindeutigen Anzeigenamen für die Eigenschaft
  4. Fügen Sie ein ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, oder INDEXLEN_(ob LEN_oderLENGTH_ persönliche Vorliebe) aufgezählt Variable am Ende. Sie sollten diese Variable nach Möglichkeit in Ihrem Code verwenden, um sicherzustellen, dass das Hinzufügen eines zusätzlichen Eintrags zur Aufzählung und das Erhöhen dieses Werts Ihren Code nicht beschädigt.
  5. Geben Sie jeden aufeinander folgenden Aufzählungs Variable einen Wert eins mehr als die letzten, beginnend bei 0. Es gibt Kommentare auf dieser Seite , dass sagen sie 0sollen nicht als Aufzählungswert verwendet werden , da 0 == null, 0 == false, 0 == ""und andere JS Verrücktheit. Ich sage Ihnen, um dieses Problem zu vermeiden und gleichzeitig die Leistung zu steigern, verwenden Sie immer ===und lassen ==Sie es nie in Ihrem Code erscheinen, außer mit typeof(ex typeof X == "string"). In all meinen Jahren ===hatte ich noch nie ein Problem damit, 0 als Aufzählungswert zu verwenden. Wenn Sie immer noch zimperlich sind, 1kann dies in vielen Fällen als Startwert in ENUM_Aufzählungen (aber nicht in INDEX_Aufzählungen) ohne Leistungseinbußen verwendet werden.
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
}

So erinnere ich mich, wann INDEX_und wann ich es verwenden soll ENUM_:

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

Jedoch ENUM_kann unter bestimmten Umständen angemessen sein , als Index, wie wenn die Vorkommen der einzelnen Elemente zu zählen.

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

Beachten Sie, dass es im obigen Code wirklich einfach ist, eine neue Art von Haustier hinzuzufügen: Sie müssten lediglich einen neuen Eintrag anhängen ENUM_PET_RATund entsprechend aktualisieren ENUMLEN_PET. Es könnte schwieriger und fehlerhafter sein, einen neuen Eintrag in andere Aufzählungssysteme einzufügen.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwv wvwv

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

Darüber hinaus ermöglicht diese Syntax von Aufzählungen eine klare und präzise Klassenerweiterung, wie unten dargestellt. Um eine Klasse zu erweitern, fügen Sie dem LEN_Eintrag der übergeordneten Klasse eine inkrementelle Nummer hinzu . Beenden Sie dann die Unterklasse mit einem eigenen LEN_Eintrag, damit die Unterklasse in Zukunft weiter erweitert werden kann.

Zusatzerweiterungsdiagramm

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

(Länge: 2.450 Byte)

Einige mögen sagen, dass dies weniger praktisch ist als andere Lösungen: Es beansprucht jede Menge Platz, das Schreiben dauert lange und es ist nicht mit Zuckersyntax beschichtet. Diese Leute hätten Recht, wenn sie ihren Code nicht minimieren würden. Keine vernünftige Person würde jedoch unminimierten Code im Endprodukt belassen. Für diese Minimierung ist Closure Compiler der beste, den ich bisher gefunden habe. Den Online-Zugang finden Sie hier . Der Closure-Compiler ist in der Lage, all diese Aufzählungsdaten zu erfassen und inline zu machen, sodass Ihr Javascript sehr klein ist und sehr schnell läuft. Minimieren Sie daher mit Closure Compiler. Beobachten.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwv wvwv

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

Der Closure-Compiler kann einige ziemlich unglaubliche Optimierungen über Schlussfolgerungen durchführen, die weit über die Kapazitäten anderer Javascript-Minifier hinausgehen. Closure Compiler kann primitive Variablen inline setzen, die auf einen festen Wert festgelegt sind. Closure Compiler kann auch basierend auf diesen Inline-Werten Rückschlüsse ziehen und nicht verwendete Blöcke in if-Anweisungen und Schleifen entfernen.

Wringing-Code über 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);

(Länge: 605 Bytes)

Closure Compiler belohnt Sie dafür, dass Sie intelligenter codieren und Ihren Code gut organisieren, denn während viele Minifahrer organisierten Code mit einer größeren minimierten Dateigröße bestrafen, kann Closure Compiler all Ihre Sauberkeit und Vernunft durchgehen, um eine noch kleinere Dateigröße auszugeben, wenn Sie Tricks anwenden wie Aufzählungen von Variablennamen. Dies ist in diesem Sinne der heilige Gral des Codierens: Ein Tool, das sowohl Ihren Code mit einer kleineren minimierten Größe unterstützt als auch Ihren Geist unterstützt, indem es bessere Programmiergewohnheiten trainiert.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwv wvwv

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

Lassen Sie uns nun sehen, wie groß die entsprechende Datei ohne eine dieser Aufzählungen wäre.

Quelle ohne Verwendung von Aufzählungen (Länge: 1.973 Byte (477 Byte kürzer als aufgezählter Code!))
Minimiert ohne Verwendung von Aufzählungen (Länge: 843 Byte (238 Byte länger als aufgezählter Code ))

Diagramm der Codegrößen



Wie zu sehen ist, ist der Quellcode ohne Aufzählungen auf Kosten eines größeren minimierten Codes kürzer. Ich kenn dich nicht wirklich; Ich weiß jedoch mit Sicherheit, dass ich keinen Quellcode in das Endprodukt einbinde. Daher ist diese Form der Aufzählung insofern weit überlegen, als sie zu kleineren minimierten Dateigrößen führt.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwv wvwv

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

Ein weiterer Vorteil dieser Form der Aufzählung besteht darin, dass damit große Projekte einfach verwaltet werden können, ohne die minimierte Codegröße zu beeinträchtigen. Wenn Sie an einem großen Projekt mit vielen anderen Personen arbeiten, kann es hilfreich sein, die Variablennamen explizit zu markieren und zu kennzeichnen, mit denen der Code erstellt wurde, damit der ursprüngliche Ersteller des Codes für die gemeinsame Fehlerbehebung schnell identifiziert werden kann.

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

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

Ferner ist diese Form der Aufzählung auch nach der Minimierung viel schneller. In normal benannten Eigenschaften muss der Browser Hashmaps verwenden, um nachzuschlagen, wo sich die Eigenschaft auf dem Objekt befindet. Obwohl JIT-Compiler diesen Speicherort auf dem Objekt intelligent zwischenspeichern, entsteht aufgrund von Sonderfällen wie dem Löschen einer niedrigeren Eigenschaft aus dem Objekt immer noch ein enormer Overhead.

Bei kontinuierlichen, nicht spärlichen, ganzzahlig indizierten PACKED_ELEMENTS- Arrays kann der Browser jedoch einen Großteil dieses Overheads überspringen, da der Index des Werts im internen Array bereits angegeben ist. Ja, gemäß dem ECMAScript-Standard sollen alle Eigenschaften als Zeichenfolgen behandelt werden. Trotzdem ist dieser Aspekt des ECMAScript-Standards in Bezug auf die Leistung sehr irreführend, da alle Browser spezielle Optimierungen für numerische Indizes in Arrays haben.

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

Vergleichen Sie den obigen Code mit dem folgenden Code.

/// 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";

Man könnte gegen den Code Einwände erheben, deren Aufzählungen viel länger zu sein scheinen als der Code mit gewöhnlichen Objekten, aber das Aussehen kann täuschen. Es ist wichtig zu beachten, dass die Quellcodegröße bei Verwendung des epischen Closure Compilers nicht proportional zur Ausgabegröße ist. Beobachten.

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

Der minimierte Code ohne Aufzählungen ist oben und der minimierte Code mit Aufzählungen ist unten.

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

Das obige Beispiel zeigt, dass der aufgezählte Code nicht nur eine überlegene Leistung aufweist, sondern auch zu einer kleineren minimierten Dateigröße führt.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwv wvwv

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

Darüber hinaus verwendet diese persönliche Kirsche oben diese Form der Aufzählung zusammen mit dem CodeMirror- Texteditor im Javascript-Modus. Der Hervorhebungsmodus für die Javascript-Syntax von CodeMirror hebt lokale Variablen im aktuellen Bereich hervor. Auf diese Weise wissen Sie sofort, wann Sie einen Variablennamen korrekt , da der Variablenname zuvor mit der deklariert wurde,var Schlüsselwort deklariert wurde, erhält der Variablenname eine Sonderfarbe (standardmäßig Cyan). Auch wenn Sie CodeMirror nicht verwenden, ist zumindest der Browser hilfreich[variable name] is not definedAusnahme beim Ausführen von Code mit falsch eingegebenen Aufzählungsnamen. JavaScript-Tools wie JSLint und Closure Compiler sagen Ihnen sehr laut, wenn Sie einen Namen für eine Aufzählungsvariable falsch eingeben. CodeMirror, der Browser und verschiedene Javascript-Tools zusammen machen das Debuggen dieser Form der Aufzählung sehr einfach und sehr einfach.

Demonstration zur Hervorhebung von 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
}

Im obigen Snippet wurden Sie mit einem Fehler benachrichtigt, da dieser ENUM_COLORENUM_DNEnicht vorhanden ist.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwv wvwv

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Ich denke, man kann mit Sicherheit sagen, dass diese Aufzählungsmethode in der Tat der beste Weg ist, nicht nur für eine minimierte Codegröße, sondern auch für Leistung, Debugging und Zusammenarbeit.

Nachdem ich eine hilfreiche Frage gelesen habe, danke ich dem Autor, dass er Zeit in das Schreiben gesteckt hat, indem er auf den Pfeil oben links im Fragenfeld geklickt hat. Jedes Antwortfeld hat auch einen dieser Aufwärtspfeile.

Jack Giffin
quelle
Eh. Ich bevorzuge die Lesbarkeit, Benutzerfreundlichkeit und das Verständnis der Codegröße.
Andrew
1
@ Andrew Mit meiner Antwort können Sie beides haben. Meine Antwort ergibt den am einfachsten zu verwendenden / zu verwaltenden Code und die kleinste minimierte Codegröße.
Jack Giffin
1
@ Andrew Ich habe versucht, Ihre Noch eine Aufzählung (JA!) Auf das Farbparser-Beispiel in meiner Antwort anzuwenden . Ich habe jedoch einige Probleme gefunden, die Sie möglicherweise beheben möchten. YEA hat keine Möglichkeit, Aufzählungen mit Unterklassen zu erweitern, was mich dazu zwingt, separate Eltern- und Kinderklassen zu erstellen, was bei großen Projekten sehr schwierig sein kann. YEA stellt nicht sicher, dass der Eintrag vorhanden ist (ex colors.REEDRenditen undefined), daher führen Tippfehler zu schwer fassbaren Rätseln. YEA unterscheidet nicht zwischen der Verwendung von Aufzählungen als Indizes und IDs, was zu verwirrendem Code führt, bei dem alles gleich aussieht. …
Jack Giffin
1
@ Andrew… YEA behindert die Minimierungsfähigkeit von Closure Compiler. Vergleichen Sie den Quellcode mit YEA (3549 Byte) mit dem minimierten Code mit YEA (1344 Byte) mit dem minimierten Code mit meiner Lösung (604 Byte). Schließlich beinhaltet YEA "Zuordnung nach Namen", da Zeichenfolgennamen von aufgezählten IDs getrennt werden. Meins berücksichtigt nur die ID, daher ist keine "Zuordnung nach Namen" erforderlich, was zu einem einfacheren Design und einer besseren Leistung führt. Vielen Dank, dass Sie Ihre Lösung geteilt haben, aber es sind viele Korrekturen erforderlich, bevor sie praktisch sein kann.
Jack Giffin
1
@ Andrew Du hast ein Recht auf deine Meinung wie ich auf meine 👍
Jack Giffin
23

Ich habe damit herumgespielt, weil ich meine Aufzählungen liebe. =)

Mit Object.definePropertyIch denke, ich habe eine etwas praktikable Lösung gefunden.

Hier ist eine jsfiddle: http://jsfiddle.net/ZV4A6/

Mit dieser Methode sollten Sie (theoretisch) in der Lage sein, Aufzählungswerte für jedes Objekt aufzurufen und zu definieren, ohne andere Attribute dieses Objekts zu beeinflussen.

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

Wegen des Attributs writable:falsedies sollte es sicher geben machen.

Sie sollten also in der Lage sein, ein benutzerdefiniertes Objekt zu erstellen und es dann aufzurufen Enum(). Die zugewiesenen Werte beginnen bei 0 und erhöhen sich pro Element.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3
Duncan
quelle
3
Wenn Sie return this;am Ende von Enum hinzufügen, können Sie Folgendes tun:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP
Ich habe das nicht in Betracht gezogen, da es nicht meine normale Methode ist, Dinge zu tun. Aber du bist absolut richtig! Ich werde das in bearbeiten.
Duncan
Ich mag das wirklich, obwohl ich kein großer Fan davon bin, den Objektraum (mit der globalen Funktion ENUM) durcheinander zu bringen. Konvertierte dies in eine Mkenum-Funktion und fügte optionale numerische Zuweisungen hinzu => var MixedUp = Mkenum ('SCHWARZ', {ROT: 0x0F00, BLAU: 0X0F, GRÜN: 0x0F0, WEISS: 0x0FFF, EINS: 1}, ZWEI, DREI, VIER) ;; // Füge meinen Code als Antwort unten hinzu. Vielen Dank.
Andrew Philips
Um ehrlich zu sein, benutze ich das nicht einmal mehr. Ich habe den Closure Compiler von Google verwendet, und dies funktioniert nicht so gut (oder kompliziert die Dinge nur), wenn Sie die Einstellung "Erweitert" verwenden. Also bin ich gerade zur Standardobjektnotation zurückgekehrt.
Duncan
1
falseist die Standardeinstellung für writable, enumerableund configurable. Keine Notwendigkeit, über Standardeinstellungen zu kauen.
Ceving
23

Verwenden Sie Javascript- Proxies

TLDR: Fügen Sie diese Klasse zu Ihren Dienstprogrammmethoden hinzu und verwenden Sie sie im gesamten Code. Sie verspottet das Enum-Verhalten traditioneller Programmiersprachen und löst tatsächlich Fehler aus, wenn Sie versuchen, entweder auf einen nicht vorhandenen Enumerator zuzugreifen oder einen Enumerator hinzuzufügen / zu aktualisieren. Sie müssen sich nicht darauf verlassen 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);
  }
}

Erstellen Sie dann Aufzählungen, indem Sie die Klasse instanziieren:

const roles = new Enum({
  ADMIN: 'Admin',
  USER: 'User',
});

Vollständige Erklärung:

Eine sehr nützliche Funktion von Enums, die Sie aus traditionellen Sprachen erhalten, ist, dass sie explodieren (einen Fehler beim Kompilieren auslösen), wenn Sie versuchen, auf einen Enumerator zuzugreifen, der nicht vorhanden ist.

Abgesehen davon, dass die verspottete Enum-Struktur eingefroren wird, um zu verhindern, dass versehentlich / böswillig zusätzliche Werte hinzugefügt werden, befasst sich keine der anderen Antworten mit dieser intrinsischen Funktion von Enums.

Wie Sie wahrscheinlich wissen, wird der Zugriff auf nicht vorhandene Mitglieder in JavaScript einfach zurückgegeben undefinedund Ihr Code wird nicht in die Luft gesprengt. Da Enumeratoren vordefinierte Konstanten sind (dh Wochentage), sollte es niemals einen Fall geben, in dem ein Enumerator undefiniert sein sollte.

Verstehen Sie mich nicht falsch, das Rückgabeverhalten von JavaScript undefinedbeim Zugriff auf undefinierte Eigenschaften ist tatsächlich ein sehr leistungsfähiges Merkmal der Sprache, aber es ist kein Merkmal, das Sie möchten, wenn Sie versuchen, traditionelle Enum-Strukturen zu verspotten.

Hier leuchten Proxy-Objekte. Mit der Einführung von ES6 (ES2015) wurden die Proxies in der Sprache standardisiert. Hier ist die Beschreibung von MDN:

Das Proxy-Objekt wird verwendet, um benutzerdefiniertes Verhalten für grundlegende Operationen zu definieren (z. B. Suche nach Eigenschaften, Zuweisung, Aufzählung, Funktionsaufruf usw.).

Ähnlich wie bei einem Webserver-Proxy können JavaScript-Proxys Operationen an Objekten abfangen (unter Verwendung von "Traps", nennen Sie sie Hooks, wenn Sie möchten) und verschiedene Überprüfungen, Aktionen und / oder Manipulationen durchführen, bevor sie abgeschlossen sind (oder In einigen Fällen werden die Operationen insgesamt gestoppt. Dies ist genau das, was wir tun möchten, wenn wir versuchen, auf einen nicht vorhandenen Enumerator zu verweisen.

Hier ist ein erfundenes Beispiel, das das Proxy-Objekt verwendet, um Aufzählungen nachzuahmen. Die Enumeratoren in diesem Beispiel sind Standard-HTTP-Methoden (dh "GET", "POST" usw.):

// Class for creating enums (13 lines)
// Feel free to add this to your utility library in 
// your codebase and profit! Note: As Proxies are an ES6 
// feature, some browsers/clients may not support it and 
// you may need to transpile using a service like babel

class Enum {
  // The Enum class instantiates a JavaScript Proxy object.
  // Instantiating a `Proxy` object requires two parameters, 
  // a `target` object and a `handler`. We first define the handler,
  // then use the handler to instantiate a Proxy.

  // A proxy handler is simply an object whose properties
  // are functions which define the behavior of the proxy 
  // when an operation is performed on it. 
  
  // For enums, we need to define behavior that lets us check what enumerator
  // is being accessed and what enumerator is being set. This can be done by 
  // defining "get" and "set" traps.
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name]
        }
        throw new Error(`No such enumerator: ${name}`)
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    }


    // Freeze the target object to prevent modifications
    return new Proxy(enumObj, handler)
  }
}


// Now that we have a generic way of creating Enums, lets create our first Enum!
const httpMethods = new Enum({
  DELETE: "DELETE",
  GET: "GET",
  OPTIONS: "OPTIONS",
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT"
})

// Sanity checks
console.log(httpMethods.DELETE)
// logs "DELETE"

try {
  httpMethods.delete = "delete"
} catch (e) {
console.log("Error: ", e.message)
}
// throws "Cannot add/update properties on an Enum instance after it is defined"

try {
  console.log(httpMethods.delete)
} catch (e) {
  console.log("Error: ", e.message)
}
// throws "No such enumerator: delete"


ASIDE: Was zum Teufel ist ein Proxy?

Ich erinnere mich, als ich das Wort Proxy zum ersten Mal überall sah, machte es für mich lange Zeit definitiv keinen Sinn. Wenn Sie es gerade sind, besteht eine einfache Möglichkeit, Proxys zu verallgemeinern, darin, sie als Software, Institutionen oder sogar als Personen zu betrachten, die als Vermittler oder Zwischenhändler zwischen zwei Servern, Unternehmen oder Personen fungieren.

Govind Rai
quelle
Wie mache ich so etwas wie myEnum.valueOf ("someStringValue")? Erwartet: Falls die Eingabezeichenfolge den Wert eines Elements des Enumerators enthält, sollte das Element zurückgegeben werden. Wenn kein Element diesen Zeichenfolgenwert hat, lösen Sie eine Ausnahme aus.
sscarduzio
@sscarduzio Sie können die Standardmethode überschreiben, valueOfindem Sie sie als Instanzmethode in der Enum-Klasse angeben. Warum möchten Sie jedoch auf diese Weise darauf zugreifen, anstatt nur über die Punktnotation darauf zuzugreifen?
Govind Rai
Meine Aufzählung ist const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}) und ich analysiere von der Eingabe eine beliebige Zeichenfolge "info" oder "debug". Also brauche ich so etwas wie currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio
1
Warum konntest du es nicht einfach tun logLevelEnum[settings.get("log_level")]? Das Hinzufügen parseOrThrowwürde sich nur wiederholen, was die Proxy-Traps bereits für Sie tun.
Govind Rai
17

Dies ist eine alte, die ich kenne, aber die Art und Weise, wie sie seitdem über die TypeScript-Oberfläche implementiert wurde, ist:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Foo"] = 0] = "Foo";
    MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
    MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));

Auf diese Weise können Sie unabhängig von der Reihenfolge der Deklaration nachsehen, MyEnum.Barwelche 1 und MyEnum[1]welche "Bar" zurückgibt.

Rob Hardy
quelle
1
Plus MyEnum ["Bar"] funktioniert, was 1 ... <3 TypeScript bisher
zurückgibt
3
und natürlich, wenn Sie tatsächlich Typoskript verwenden:enum MyEnum { Foo, Bar, Foobar }
Parlament
16

In ES7 können Sie eine elegante ENUM erstellen , die auf statischen Attributen basiert :

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

dann

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

Der Vorteil (Verwendung einer Klasse anstelle eines EnumLiteralobjekts ) besteht darin, dass eine übergeordnete Klasse vorhanden ist. Alle Ihre Enums erweitern diese Klasse.

 class ColorEnum  extends Enum {/*....*/}
Abdennour TOUMI
quelle
4
Können Sie bitte erklären, warum eine Elternklasse von Vorteil ist? Ich habe das Gefühl, ich vermisse etwas!
Jon G
7
Tu das nicht. new ColorEnum()macht absolut keinen Sinn.
Bergi
3
Eine Aufzählung zu verlängern klingt wirklich verrückt
Codii
Sobald die Sprache sie nicht nativ unterstützt, wäre es sinnvoll, diese Konvention beizubehalten und so zu verwenden! Genau!
Bis zum
Ich denke , was OP ist immer an, ist: Der Vorteil der rein statisch ist , dass es überall als Singleton verfügbar ist, und Sie müssen nicht (?) Müssen die Klasse instanziiert - OP ist nicht darauf hindeutet , dass Sie tun! Ich denke , was er sagt , ist , dass die übergeordnete Klasse EnumStandard hat statische enumerator Methoden auf sich, wie getValues(), getNames(), iterate()usw. Wenn das der Fall ist, müssen Sie sie nicht für jede neue Art von neu zu implementieren enum.
Ingenieur
15

Dies ist die Lösung, die ich benutze.

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

Und Sie definieren Ihre Aufzählungen wie folgt:

var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

Und so greifen Sie auf Ihre Aufzählungen zu:

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

Normalerweise verwende ich die letzten beiden Methoden, um Aufzählungen von Nachrichtenobjekten zuzuordnen.

Einige Vorteile dieses Ansatzes:

  • Einfach zu deklarierende Aufzählungen
  • Einfacher Zugriff auf Ihre Aufzählungen
  • Ihre Aufzählungen können komplexe Typen sein
  • Die Enum-Klasse verfügt über assoziatives Caching, wenn Sie häufig getByValue verwenden

Einige Nachteile:

  • Dort läuft eine unordentliche Speicherverwaltung ab, da ich die Verweise auf die Aufzählungen behalte
  • Immer noch keine Typensicherheit
Chris
quelle
14

Erstellen Sie ein Objektliteral:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
hvdd
quelle
12
constmacht die Eigenschaften des Objekts nicht unveränderlich, sondern bedeutet nur, dass die Variable Modesnicht etwas anderem zugewiesen werden kann. Um es vollständiger zu machen, verwenden Sie Object.freeze()neben const.
Rvighne
Bitte nicht verwenden Object.freeze. Es verhindert, dass der Closure Compiler das Objekt einbindet.
Jack Giffin
11

Wenn Sie mit Backbone , können Sie ausgewachsene ENUM - Funktionalität (finden von id, name, benutzerdefinierte Mitglieder) kostenlos mit bekommen 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()
Jaroslaw
quelle
8

Ihre Antworten sind viel zu kompliziert

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
quelle
1
@JackGiffin Ich stimme zu, dass Ihre Antwort leistungsfähiger ist und dass meine möglicherweise mehr Speicherplatz beansprucht, obwohl Sie nicht davon ausgehen sollten, dass jeder eine Aufzählung so wünscht, wie C ++ sie implementiert hat. Bitte respektieren Sie andere Antworten und die Entwickler, die diese möglicherweise Ihren vorziehen.
Xeltor
7

Ich habe die Lösung von Andre 'Fi' geändert:

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

Prüfung:

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ó
quelle
6

Ich habe mir diesen Ansatz ausgedacht, der Enums in Java nachempfunden ist. Diese sind typsicher, sodass Sie auch instanceofÜberprüfungen durchführen können .

Sie können Aufzählungen wie folgt definieren:

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

Daysbezieht sich jetzt auf die DaysAufzählung:

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"

Die Umsetzung:

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
quelle
Es sieht gut aus, vielleicht sollten Sie die Existenz der freezeMethode auf Abwärtskompatibilität prüfen ? ZBif (Object.freeze) { Object.freeze(values); }
FBB
Guter Punkt! Wird besorgt!
Vivin Paliath
6

IE8 unterstützt die Methode freeze () nicht.
Quelle: http://kangax.github.io/compat-table/es5/ , Klicken Sie auf "Veraltete Browser anzeigen?" oben und überprüfen Sie IE8 und frieren Sie die Kreuzungskreuzung ein.

In meinem aktuellen Spielprojekt habe ich unten verwendet, da nur noch wenige Kunden IE8 verwenden:

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

Wir könnten auch tun:

var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

oder sogar das:

var CONST_WILD_TYPES = {
    REGULAR: '1',
    EXPANDING: '2',
    STICKY: '3',
    SHIFTING: '4'
};

Die letzte Option scheint für Zeichenfolgen am effizientesten zu sein. Sie reduziert Ihre Gesamtbandbreite, wenn Server und Client diese Daten austauschen.
Natürlich ist es jetzt Ihre Pflicht, sicherzustellen, dass die Daten keine Konflikte enthalten (RE, EX usw. müssen eindeutig sein, auch 1, 2 usw. sollten eindeutig sein). Beachten Sie, dass Sie diese aus Gründen der Abwärtskompatibilität für immer beibehalten müssen.

Zuordnung:

var wildType = CONST_WILD_TYPES.REGULAR;

Vergleich:

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

Sie müssen nicht sicherstellen, dass Sie auf diese Weise keinen doppelten Zahlen unterschiedliche Aufzählungswerte zuweisen. Ein neues Objekt wird instanziiert und allen Aufzählungswerten zugewiesen.

Shivanshu Goyal
quelle
Diese Antwort wird unterschätzt. Es ist eine meiner Lieblingsideen für seine Einfachheit. In der Praxis denke ich, dass ich mich an Strings halten werde, da das Debuggen vorerst einfacher ist.
Domino
Hmm, stellen Sie nur sicher, dass dieser Code nicht zweimal aufgerufen wird ...
Andrew
4

Hier sind einige verschiedene Möglichkeiten, um TypeScript-Aufzählungen zu implementieren .

Am einfachsten ist es, einfach über ein Objekt zu iterieren und dem Objekt invertierte Schlüssel-Wert-Paare hinzuzufügen. Der einzige Nachteil ist, dass Sie den Wert für jedes Mitglied manuell festlegen müssen.

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


Und hier ist ein Lodash-Mixin , um mit einer Zeichenfolge eine Aufzählung zu erstellen. Während diese Version etwas komplizierter ist, übernimmt sie die Nummerierung automatisch für Sie. Alle in diesem Beispiel verwendeten lodash-Methoden haben ein reguläres JavaScript-Äquivalent, sodass Sie sie bei Bedarf problemlos austauschen können.

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
quelle
sehr klug, danke
Ilan
4

Ich habe gerade ein NPM-Paket veröffentlicht. Mit gen_enum können Sie schnell eine Enum-Datenstruktur in Javascript erstellen:

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 

Eine schöne Sache an diesem kleinen Tool ist, dass in einer modernen Umgebung (einschließlich NodeJS und IE 9+ Browsern) das zurückgegebene Enum-Objekt unveränderlich ist.

Weitere Informationen finden Sie unter https://github.com/greenlaw110/enumjs

Aktualisierung

Ich veraltete das gen_enumPaket und füge die Funktion in das constjs- Paket ein, das weitere Funktionen bietet, darunter unveränderliche Objekte, JSON-String-Deserialisierung, String-Konstanten und Bitmap-Generierung usw. Checkout https://www.npmjs.com/package/constjs Weitere Informationen finden Sie unter

Um ein Upgrade von gen_enumauf constjsnur die Aussage ändern

var genEnum = require('gen_enum');

zu

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

Einfachste Lösung:

Erstellen

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

Wert erhalten

console.log(Status.Ready) // 1

Schlüssel holen

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

Ich habe eine Enum-Klasse erstellt, die Werte UND Namen bei O (1) abrufen kann. Es kann auch ein Objektarray generiert werden, das alle Namen und Werte enthält.

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

Sie können es so initiieren:

var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

So rufen Sie einen Wert ab (wie Enums in C #):

var val2 = enum1.item2;

So rufen Sie einen Namen für einen Wert ab (kann mehrdeutig sein, wenn Sie denselben Wert für verschiedene Namen eingeben):

var name1 = enum1.GetName(0);  // "item1"

So erhalten Sie ein Array mit jedem Namen und Wert in einem Objekt:

var arr = enum1.GetObjArr();

Wird generieren:

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

Sie können auch die HTML-Auswahloptionen leicht erhalten:

var html = enum1.GetSelectOptionsHTML();

Was gilt:

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

Obwohl in ES2015 nur statische Methoden (und keine statischen Eigenschaften) unterstützt werden (siehe auch hier , §15.2.2.2), können Sie seltsamerweise das Folgende mit Babel mit der es2015Voreinstellung verwenden:

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

Ich fand, dass dies auch über Module hinweg wie erwartet funktioniert (z. B. Importieren der CellStateEnumeration von einem anderen Modul) und auch, wenn ich ein Modul mit Webpack importiere.

Der Vorteil dieser Methode gegenüber den meisten anderen Antworten besteht darin, dass Sie sie zusammen mit einer statischen Typprüfung (z. B. Flow ) verwenden können und zur Entwicklungszeit mithilfe der statischen Typprüfung behaupten können, dass Ihre Variablen, Parameter usw. spezifisch sind. CellState" enum "anstelle einer anderen Aufzählung (die nicht zu unterscheiden wäre, wenn Sie generische Objekte oder Symbole verwenden würden).

aktualisieren

Der obige Code hat den Nachteil, dass man zusätzliche Objekte vom Typ erstellen CellStatekann (obwohl man sie nicht den statischen Feldern von zuordnen kann, CellStateda sie eingefroren sind). Der unten verfeinerte Code bietet jedoch die folgenden Vorteile:

  1. keine Objekte mehr vom Typ CellState mehr erstellt werden
  2. Ihnen wird garantiert, dass keinen zwei Enum-Instanzen der gleiche Code zugewiesen wird
  3. Utility-Methode, um die Aufzählung aus einer Zeichenfolgendarstellung zurückzugewinnen
  4. Die valuesFunktion, die alle Instanzen der Aufzählung zurückgibt, muss den Rückgabewert nicht auf die oben beschriebene manuelle (und fehleranfällige) Weise erstellen.

    '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
quelle
Gutes Beispiel :-)
Ashraf.Shk786
4

es7 way, (Iterator, Freeze), Verwendung:

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
quelle
4

So übersetzt Typescript es enumin Javascript:

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

Jetzt:

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

Zuerst war ich verwirrt, warum obj[1]zurückgegeben wird 'Active', aber dann wurde mir klar, dass es absolut einfach ist - Zuweisungsoperator weist Wert zu und gibt ihn dann zurück:

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

Sie können so etwas tun

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

Wie in dieser Bibliothek definiert. https://github.com/webmodule/foo/blob/master/foo.js#L217

Vollständiges Beispiel https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026

LNT
quelle
3

Ein schneller und einfacher Weg wäre:

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

console.log(Colors.WHITE)  //this prints out "0"
user2254487
quelle
6
Die Funktion ist nicht erforderlich und liefert genau das gleiche Ergebnis wie das OP.
Sildoreth
3

Stand: Oktober 2014 - hier ist also eine zeitgemäße Lösung. Ich schreibe die Lösung als Knotenmodul und habe einen Test mit Mocha und Chai sowie einen Unterstrich aufgenommen. Sie können diese leicht ignorieren und bei Bedarf einfach den Enum-Code verwenden.

Ich habe viele Posts mit übermäßig verschlungenen Bibliotheken usw. gesehen. Die Lösung, um Enum-Unterstützung in Javascript zu erhalten, ist so einfach, dass sie wirklich nicht benötigt wird. Hier ist der Code:

Datei: 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;

Und ein Test, um zu veranschaulichen, was es Ihnen gibt:

Datei: 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');
        });
    });

});

Wie Sie sehen können, erhalten Sie eine Enum-Factory, Sie können alle Schlüssel einfach durch Aufrufen von enum.keys abrufen und Sie können die Schlüssel selbst mit ganzzahligen Konstanten abgleichen. Sie können die Factory mit unterschiedlichen Werten wiederverwenden und die generierten Enums mithilfe des modularen Ansatzes von Node exportieren.

Wenn Sie nur ein gelegentlicher Benutzer sind oder im Browser usw., nehmen Sie einfach den werkseitigen Teil des Codes und entfernen Sie möglicherweise auch die Unterstrichbibliothek, wenn Sie ihn nicht in Ihrem Code verwenden möchten.

arcseldon
quelle
5
Könnten Sie eine Antwort mit nur "Hier erfahren Sie, wie Sie dies als Gelegenheitsbenutzer tun, der nur Aufzählungen, keine Fabriken, Unterstriche oder etwas Besonderes möchte" posten?
GreenAsJade
5
Obwohl dies von einem Entwickler-Augenpunkt aus ziemlich beeindruckend ist, ist es nicht sehr sauber oder lesbar. Die Enum-Lösung aus dem OP ist in jeder Hinsicht einfacher und lesbarer und daher besser zu verwenden. Trotzdem ziemlich beeindruckend, dass Sie sich das ausgedacht haben.
David
3

Es ist einfach zu bedienen, denke ich. 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);

AKTUALISIEREN:

Es gibt meine Hilfscodes ( TypeHelper).

Sherali Turdiyev
quelle