Vraag Wat is de voorkeurssyntaxis voor het definiëren van enums in JavaScript?


Wat is de voorkeurssyntaxis voor het definiëren van enums in JavaScript? Zoiets als:

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

// later on

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

Of is er een beter idioom?


1675
2017-11-13 19:09


oorsprong


antwoorden:


Sinds 1.8.5 is het mogelijk om het object te verzegelen en te bevriezen, dus definieer het bovenstaande als:

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

of

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

en voila! JS-enums.

Dit neemt echter niet weg dat u een ongewenste waarde toewijst aan een variabele, wat vaak het belangrijkste doel van de Enums is:

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

Een manier om een ​​sterkere mate van typeveiligheid (met enums of anderszins) te garanderen, is door een tool als te gebruiken getypte tekst of Stroom.

Bron

Citaten zijn niet nodig, maar ik heb ze bewaard voor consistentie.


539
2018-02-18 11:03



Dit is niet echt een antwoord, maar ik zou zeggen dat het prima werkt, persoonlijk

Dat gezegd hebbende, omdat het niet uitmaakt wat de waarden zijn (je hebt 0, 1, 2 gebruikt), zou ik een zinvolle string gebruiken voor het geval je ooit de huidige waarde wilde uitvoeren.


583
2017-11-13 19:13



BIJWERKEN: Bedankt voor alle upvotes iedereen, maar ik denk niet dat mijn antwoord hieronder de beste manier is om enums in Javascript te schrijven. Zie mijn blogpost voor meer details: Enums in Javascript.


Het melden van de naam is al mogelijk:

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

Je kunt ook de waarden objecten maken, zodat je de taart kunt pakken en deze ook kunt eten:

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, omdat het een dynamische taal is, is het zelfs mogelijk om opsommingstekens later aan de set toe te voegen:

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

Let op, de velden van het enum (waarde, naam en code in dit voorbeeld) zijn niet nodig voor de identiteitscontrole en zijn er alleen voor het gemak. Ook hoeft de naam van de eigenschap size zelf niet hardcoded te zijn, maar kan deze ook dynamisch worden ingesteld. Dus stel dat u alleen de naam kent voor uw nieuwe enum-waarde, dan kunt u deze nog steeds zonder problemen toevoegen:

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

Dit betekent natuurlijk dat sommige aannames niet meer kunnen worden gemaakt (die waarde vertegenwoordigt bijvoorbeeld de juiste volgorde voor de grootte).

Houd er rekening mee dat een object in Javascript net een kaart of een hashtabel is. Een set naam / waarde-paren. Je kunt er doorheen lopen of ze op een andere manier manipuleren zonder van tevoren al veel van hen te weten.

bijv:

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

En trouwens, als u geïnteresseerd bent in naamruimten, wilt u misschien eens kijken naar mijn oplossing voor eenvoudig maar krachtig beheer van naamruimten en afhankelijkheden voor javascript: Pakketten JS


477
2018-03-04 22:31



Kort gezegd: dat kan niet.

Je kunt het nep maken, maar je krijgt geen typeveiligheid. Meestal wordt dit gedaan door een eenvoudig woordenboek te maken met tekenreekswaarden die zijn toegewezen aan gehele getallen. Bijvoorbeeld:

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

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

Het probleem met deze aanpak? U kunt per ongeluk uw aanvrager opnieuw definiëren, of per ongeluk dubbele enumerante waarden gebruiken. Bijvoorbeeld:

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

Bewerk 

Hoe zit het met Object.freeze van Artur Czajka? Zou dat niet werken om te voorkomen dat je maandag tot donderdag gaat? - Fry Quad

Absoluut, Object.freeze zou het probleem volledig oplossen waar ik over klaagde. Ik zou iedereen eraan willen herinneren dat toen ik het bovenstaande schreef, Object.freeze bestond niet echt.

Nu ... nu opent het wat heel interessante mogelijkheden.

Bewerk 2
Hier is een zeer goede bibliotheek voor het maken van enums.

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

Hoewel het waarschijnlijk niet geschikt is voor elk geldig gebruik van enums, gaat het een lange weg.


79
2017-08-21 20:56



Dit is wat we allemaal willen:

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

Nu kunt u uw enums maken:

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

Door dit te doen kunnen constanten op de gebruikelijke manier worden bewerkt (YesNo.YES, Color.GREEN) en krijgen ze een sequentiële int-waarde (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).

U kunt ook methoden toevoegen door Enum.prototype te gebruiken:

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


Bewerken - kleine verbetering - nu met varargs: (helaas werkt het niet goed op IE: S ... moet dan bij de vorige versie blijven)

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 de meeste moderne browsers is er een symbool primitief gegevenstype dat kan worden gebruikt om een ​​opsomming te maken. Hiermee wordt de veiligheid van het type enum gegarandeerd, omdat elke symboolwaarde door JavaScript wordt gegarandeerd als uniek, d.w.z. Symbol() != Symbol(). Bijvoorbeeld:

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

Om het opsporen van fouten te vereenvoudigen, kunt u een beschrijving toevoegen aan de waarden voor waarden:

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

Plunker-demo

Op GitHub je kunt een wrapper vinden die de code vereenvoudigt die nodig is om het enum te initialiseren:

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



Ik speel hier al wat mee, want ik ben dol op mijn enums. =)

Gebruik makend van Object.defineProperty Ik denk dat ik een enigszins haalbare oplossing heb bedacht.

Hier is een jsfiddle: http://jsfiddle.net/ZV4A6/

Gebruikmakend van deze methode .. zou je (in theorie) in staat moeten zijn enum-waarden voor elk object aan te roepen en te definiëren, zonder de andere attributen van dat object te beïnvloeden.

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

Vanwege het attribuut writable:false deze moeten maak het type veilig.

U moet dus in staat zijn om een ​​aangepast object te maken en vervolgens te bellen Enum() ben ermee bezig. De toegewezen waarden beginnen bij 0 en verhogen per item.

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



Dit is een oude die ik ken, maar de manier waarop het sindsdien is geïmplementeerd via de TypeScript-interface is:

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

Dit stelt u in staat om beide op te zoeken MyEnum.Bar die 1 teruggeeft, en MyEnum[1] die "balk" retourneert ongeacht de volgorde van de aangifte.


17
2018-06-24 16:11



Dit is de oplossing die ik gebruik.

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

En je definieert je enums als volgt:

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

En zo heeft u toegang tot uw Enums:

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

Ik gebruik meestal de laatste 2 methoden voor het toewijzen van enums van berichtobjecten.

Enkele voordelen van deze aanpak:

  • Eenvoudig te enums te verklaren
  • Eenvoudig toegang tot uw enums
  • Je enums kunnen complexe typen zijn
  • De Enum-klasse heeft een aantal associatieve caching als je getByValue veel gebruikt

Enkele nadelen:

  • Er zit wat rommelig geheugenbeheer in, omdat ik de verwijzingen naar de enums bewaar
  • Nog steeds geen typeveiligheid

15
2018-05-15 09:26



Als je gebruikt ruggegraat, kunt u volledig gebruik maken van de enum-functionaliteit (vind op id, naam, aangepaste leden) voor gratis gebruik Backbone.Collection.

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

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

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

// using
Colors.Red.print()

12
2018-04-24 14:04



In ES7 , u kunt een elegant ENUM maken op basis van statische kenmerken:

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

dan

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

Het voordeel (van het gebruik van klasse in plaats van het letterlijke object) is om een ​​bovenliggende klasse te hebben Enumdan zullen al je Enums dat doen strekt die klasse.

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

11
2018-01-06 07:07