Frage Was ist die bevorzugte Syntax zum Definieren von Enums in JavaScript?


Was ist die bevorzugte Syntax zum Definieren von Enums 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 ein bevorzugteres Idiom?


1675
2017-11-13 19:09


Ursprung


Antworten:


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

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

oder

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

und voila! JS enums.

Dies hindert Sie jedoch nicht daran, einer Variablen einen unerwünschten Wert zuzuweisen, was oft das Hauptziel von enums ist:

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

Eine Möglichkeit, ein höheres Maß an Sicherheit (mit oder ohne Enums) zu gewährleisten, ist die Verwendung eines Werkzeugs wie z Typoskript oder Fließen.

Quelle

Zitate werden nicht benötigt, aber ich habe sie für Konsistenz gehalten.


539
2018-02-18 11:03



Das ist keine große Antwort, aber ich würde sagen, das funktioniert ganz gut, persönlich

Da es jedoch egal ist, was die Werte sind (Sie haben 0, 1, 2 verwendet), würde ich eine sinnvolle Zeichenfolge verwenden, falls Sie den aktuellen Wert jemals ausgeben wollten.


583
2017-11-13 19:13



AKTUALISIEREN: Danke für die vielen Upvotes, aber ich glaube nicht, dass meine Antwort unten die beste Möglichkeit ist, Enums in Javascript zu schreiben. Weitere Informationen finden Sie in meinem Blogpost: Enums in Javascript.


Die Warnung 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önntest du die Werte zu Objekten machen, damit du den Kuchen essen und auch essen kannst:

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

Da es sich bei JavaScript um eine dynamische Sprache handelt, ist es sogar möglich, dem Set später enum-Werte 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 der Einfachheit halber vorhanden sind. Auch der Name der Size-Eigenschaft selbst muss nicht fest codiert sein, sondern kann auch dynamisch gesetzt werden. Wenn Sie also nur den Namen für Ihren neuen Enum-Wert kennen, können Sie ihn ohne Probleme 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 stellt beispielsweise die richtige Reihenfolge für die Größe dar).

Denken Sie daran, dass ein Objekt in Javascript genau wie eine Karte oder eine Hashtabelle ist. Eine Gruppe von Name-Wert-Paaren. Sie können sie durchlaufen oder anderweitig manipulieren, ohne vorher viel über sie zu wissen.

Z.B:

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

Und wenn du dich für Namespaces interessierst, solltest du dir vielleicht meine Lösung für einfaches, aber mächtiges Namespace- und Abhängigkeitsmanagement für Javascript ansehen: Pakete JS


477
2018-03-04 22:31



Fazit: Sie können nicht.

Sie können es vortäuschen, aber Sie erhalten keine Typsicherheit. In der Regel wird dazu ein einfaches Wörterbuch mit Stringwerten erstellt, die auf ganzzahlige Werte abgebildet sind. Beispielsweise:

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

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

Das Problem mit diesem Ansatz? Sie können Ihren Enumerator versehentlich neu definieren oder versehentlich doppelte Enumerationswerte haben. Beispielsweise:

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

Bearbeiten 

Was ist mit Artur Czajkas Object.freeze? Würde das nicht funktionieren, um dich davon abzuhalten, Montag bis Donnerstag zu setzen? - Fry Quad

Absolut, Object.freeze würde das Problem vollständig beheben, über das ich mich beschwert habe. Ich möchte jeden daran erinnern, dass ich, als ich das oben geschrieben habe, Object.freeze gab es nicht wirklich.

Jetzt .... jetzt eröffnet es einiges sehr interessante Möglichkeiten.

Bearbeiten 2
Hier ist eine sehr gute Bibliothek zum Erstellen von Enums.

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

Obwohl es wahrscheinlich nicht für jede gültige Verwendung von Enums passt, geht es sehr weit.


79
2017-08-21 20:56



Folgendes wollen wir alle:

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

Jetzt können Sie Ihre Enums erstellen:

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

Auf diese Weise können Konstanten auf die übliche Weise verarbeitet werden (YesNo.YES, Color.GREEN) und erhalten einen sequentiellen int-Wert (NO = 0, YES = 1; ROT = 0, GRÜN = 1, BLAU = 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 */
};


Edit - kleine Verbesserung - jetzt mit varargs: (leider klappt 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');

46
2017-07-13 00:28



In den meisten modernen Browsern gibt es a Symbol primitiver Datentyp, mit dem eine Aufzählung erstellt werden kann. Es wird die Typensicherheit der Enumeration sicherstellen, da jeder Symbolwert durch JavaScript als einzigartig garantiert wird, d.h. Symbol() != Symbol(). Beispielsweise:

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

Um das Debuggen zu vereinfachen, können Sie enum-Werten eine Beschreibung hinzufügen:

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

Plunker-Demo

Auf GitHub Sie können einen Wrapper finden, der den Code zum Initialisieren der Enumeration vereinfacht:

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

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

41
2018-05-05 16:32



Ich habe damit herumgespielt, weil ich meine Enums liebe. =)

Verwenden Object.defineProperty Ich denke, ich habe eine einigermaßen brauchbare Lösung gefunden.

Hier ist ein Spiel: 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:false Dies sollte Mach es sicher.

Sie sollten also in der Lage sein, ein benutzerdefiniertes Objekt zu erstellen und dann anzurufen Enum() darauf. Die zugewiesenen Werte beginnen bei 0 und werden pro Element erhöht.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

23
2017-08-21 10:34



Dies ist eine alte, die ich kenne, aber die Art, wie es seither über die TypeScript-Schnittstelle implementiert wurde, ist:

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

So können Sie beide nachschlagen MyEnum.Bar was 1, und zurückgibt MyEnum[1] was "Bar" unabhängig von der Reihenfolge der Deklaration zurückgibt.


17
2018-06-24 16:11



Das ist die Lösung, die ich verwende.

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 du definierst deine enums so:

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

Und so greifen Sie auf Ihre Enums 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
});

Ich verwende normalerweise die letzten 2 Methoden, um Enums von Nachrichtenobjekten zuzuordnen.

Einige Vorteile für diesen Ansatz:

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

Einige Nachteile:

  • Irgendeine unordentliche Speicherverwaltung geht da drinnen weiter, da ich die Referenzen zu den Enums behalte
  • Immer noch keine Art Sicherheit

15
2018-05-15 09:26



Wenn du es benutzt Rückgrat, können Sie vollständige enum-Funktionalität (Suche nach ID, Name, benutzerdefinierte Mitglieder) kostenlos mit Backbone.Kollektion.

// enum instance members, optional
var Color = Backbone.Model.extend({
    print : function() {
        console.log("I am " + this.get("name"))
    }
});

// enum creation
var Colors = new Backbone.Collection([
    { id : 1, name : "Red", rgb : 0xFF0000},
    { id : 2, name : "Green" , rgb : 0x00FF00},
    { id : 3, name : "Blue" , rgb : 0x0000FF}
], {
    model : Color
});

// Expose members through public fields.
Colors.each(function(color) {
    Colors[color.get("name")] = color;
});

// using
Colors.Red.print()

12
2018-04-24 14:04