web-dev-qa-db-ger.com

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

Was ist die bevorzugte Syntax zum Definieren von Enums in JavaScript? So 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 Redewendung?

1829
David Citron

Seit 1.8.5 ist es möglich, das Objekt zu versiegeln und einzufrieren.

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. Dies ist häufig das Hauptziel von enums:

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

Eine Möglichkeit, einen höheren Grad 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 beibehalten.

680
Artur Czajka

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

Da es sich nicht um die Werte handelt (Sie haben 0, 1, 2 verwendet), würde ich eine aussagekräftige Zeichenfolge verwenden, falls Sie den aktuellen Wert ausgeben möchten.

593
Gareth

UPDATE: Vielen Dank für alle Upvotes, aber ich denke nicht, dass meine Antwort unten der beste Weg ist, um Enummen in Javascript zu schreiben. Weitere Informationen finden Sie in meinem Blog-Beitrag: Enums in Javascript .


Alarmierung 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 Werte zu Objekten machen, damit Sie den Kuchen haben und 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, da es sich um eine dynamische Sprache handelt, ist es 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) nicht für die Identitätsprüfung benötigt werden und nur zur Vereinfachung dienen. Der Name der size-Eigenschaft selbst muss nicht hartcodiert werden, sondern kann auch dynamisch festgelegt werden. Angenommen, Sie kennen nur den Namen für Ihren neuen Aufzählungswert und können ihn trotzdem 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 gemacht werden können (dieser Wert stellt beispielsweise die richtige Reihenfolge für die Größe dar). 

Denken Sie daran, dass ein Objekt in Javascript wie eine Karte oder eine Hashtabelle ist. Eine Menge von Name-Wert-Paaren. Sie können sie durchlaufen oder auf andere Weise manipulieren, ohne vorher viel darüber 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 Sie sich für Namespaces interessieren, sollten Sie sich meine Lösung für einfache, aber leistungsfähige Namespaces und Abhängigkeitsverwaltung für Javascript ansehen: Packages JS

485
Stijn de Witt

Fazit: Das geht nicht. 

Sie können es fälschen, bekommen aber keine Sicherheit. In der Regel wird dazu ein einfaches Wörterbuch mit Zeichenfolgenwerten erstellt, die Ganzzahlwerten 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 versehentlich Ihre Aufzählung neu definieren oder versehentlich doppelte Aufzählungswerte 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? - Fry Quad 

Absolut, Object.freeze würde das Problem, über das ich mich beschwert habe, vollständig beheben. Ich möchte alle daran erinnern, dass Object.freeze nicht wirklich existierte, als ich das Vorstehende schrieb. 

Nun .... jetzt eröffnet es einige sehr interessante Möglichkeiten. 

Edit 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 Enumerationen passt, ist es ein sehr langer Weg. 

80
Randolpho

Das 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 (YesNo.YES, Color.GREEN) abgerufen werden 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 */
};


Edit - kleine Verbesserung - jetzt mit varargs: (leider funktioniert es auf IE nicht: 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');
51
Andre 'Fi'

In den meisten modernen Browsern gibt es einen Symbol -Datentyp, mit dem eine Aufzählung erstellt werden kann. Dadurch wird die Typsicherheit der Aufzählung sichergestellt, da jeder Symbolwert von JavaScript garantiert ist, eindeutig zu sein, d. H. Symbol() != Symbol(). 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 zum Initialisieren der Aufzählung erforderlichen Code 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
45

Ich habe damit rumgespielt, da ich mein Enum liebe. =)

Ich denke, ich habe mit Object.defineProperty eine etwas praktikable Lösung gefunden.

Hier ist ein 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:false dieses sollte mach es sicher.

Sie sollten also in der Lage sein, ein benutzerdefiniertes Objekt zu erstellen und dann Enum() darauf aufzurufen. 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
22
Duncan

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

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 nach MyEnum.Bar suchen, die 1 zurückgibt, und MyEnum[1], die unabhängig von der Reihenfolge der Deklaration "Bar" zurückgibt.

18
Rob Hardy

Die "Bevorzugte Syntax" der meisten Leute wurde bereits oben aufgeführt. Es gibt jedoch ein übergeordnetes Problem: die Leistung. Keine der oben genannten Antworten ist im geringsten sehr performant und alle Ihre Code-Größe wird extrem aufgebläht. Für echte Leistung, einfaches Lesen von Code und eine beispiellose Reduzierung der Codegröße durch Minifizierung ist dies die richtige Art, Aufzählungen durchzuführen.

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

// later on

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

Darüber hinaus ermöglicht diese Syntax eine klare und prägnante Erweiterung der Klasse (siehe unten).

(Länge: 2.450 Bytes)

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

    const ENUM_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          ENUMLEN_PIXELCOLOR   = 1,
          ENUM_SOLIDCOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_SOLIDCOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_SOLIDCOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUMLEN_SOLIDCOLOR   = ENUMLEN_PIXELCOLOR+3,
          ENUM_ALPHACOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_ALPHACOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_ALPHACOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUM_ALPHACOLOR_A    = ENUMLEN_PIXELCOLOR+3,
          ENUMLEN_ALPHACOLOR   = ENUMLEN_PIXELCOLOR+4,
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(rawstr) {
        rawstr = rawstr.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

Einige mögen sagen, dass dies weniger praktikabel ist als andere Lösungen: Es braucht viel Platz, das Schreiben dauert lange und es ist nicht mit Zuckersyntax überzogen. Ja, diese Leute hätten Recht, wenn sie ihren Code nicht minimieren. Keine vernünftige Person würde jedoch unbegrenzten Code im Endprodukt hinterlassen. Für diese Minifizierung ist Closure Compiler das Beste, was ich bisher gefunden habe. Online-Zugang finden Sie hier . Der Closure-Compiler ist in der Lage, all diese Aufzählungsdaten zu übernehmen und sie inline zu machen, wodurch Ihr Javascript sehr schnell läuft und super klein ist. Beobachten. 

(Länge: 605 Bytes)

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

Lassen Sie uns nun sehen, wie groß die entsprechende Datei ohne diese Aufzählung wäre.

Quelle ohne diese Aufzählungen (Länge: 1.973 Bytes (477 Bytes kürzer!))
Ohne diese Aufzählungen reduziert (Länge: 843 Bytes (238 Bytes länger))

Wie zu sehen ist, ist der Quellcode ohne Enumerationen kürzer auf Kosten eines größeren minimierten Codes. Ich weiß nicht, wie es Ihnen geht, ich weiß mit Sicherheit, dass ich keinen Quellcode in das Endprodukt integrieren möchte, was diese Aufzählung bei weitem überlegen macht, so dass kleinere Dateigrößen entstehen. Hinzu kommt, dass diese Form der Aufzählung viel schneller ist. In der Tat ist diese Form der Aufzählung der Weg zu gehen.

16
Jack Giffin

In ES7 können Sie ein elegantes ENUM mit statischen Attributen erstellen: 

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

dann 

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

Der Vorteil (Klasse anstelle von Literalobjekt) besteht darin, eine übergeordnete Klasse Enum zu haben. Dann werden alle Ihre Enums erweitert dieser Klasse. 

 class ColorEnum  extends Enum {/*....*/}
15
Abdennour TOUMI

Verwenden Sie Javascript Proxies

TLDR: Fügen Sie diese Klasse zu Ihren Dienstprogrammmethoden hinzu und verwenden Sie sie im gesamten Code. Sie macht das Verhalten der Enumeration von herkömmlichen Programmiersprachen und gibt tatsächlich Fehler aus, wenn Sie versuchen, auf einen nicht vorhandenen Enumerator zuzugreifen oder eine hinzuzufügen oder zu aktualisieren Enumerator. Sie müssen sich nicht auf Object.freeze() verlassen.

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 Enumerationen, 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 von traditionellen Sprachen erhalten, ist, dass sie aufgeblasen werden (einen Fehler bei der Kompilierung auslösen), wenn Sie versuchen, auf einen nicht vorhandenen Enumerator zuzugreifen. 

Abgesehen vom Einfrieren der simulierten Enumerationsstruktur, um zu verhindern, dass versehentlich/böswillig zusätzliche Werte hinzugefügt werden, wird in keiner der anderen Antworten auf diese intrinsische Funktion von Enums eingegangen.

Wie Sie wahrscheinlich wissen, gibt der Zugriff auf nicht vorhandene Mitglieder in JavaScript lediglich undefined zurück und führt nicht zu einer Explosion Ihres Codes. Da Enumeratoren vordefinierte Konstanten sind (d. H. Wochentage), sollte es niemals den Fall geben, dass ein Enumerator undefiniert sein sollte.

Verstehen Sie mich nicht falsch. Das Verhalten von JavaScript, undefined zurückzugeben, wenn auf undefinierte Eigenschaften zugegriffen wird, ist zwar eine sehr mächtige Funktion der Sprache, aber es ist keine Funktion, die Sie möchten, wenn Sie versuchen, herkömmliche Enum-Strukturen zu simulieren. 

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

Das Proxy-Objekt wird verwendet, um ein benutzerdefiniertes Verhalten für grundlegende Vorgänge zu definieren (z. B. Nachschlagen von Eigenschaften, Zuweisung, Aufzählung, Aufruf von Funktionen Usw.).

Ähnlich wie bei einem Web-Server-Proxy können JavaScript-Proxys Operationen an Objekten abfangen (mit Hilfe von "Traps", nennen Sie sie Hooks, wenn Sie möchten) und ermöglichen Ihnen, verschiedene Prüfungen, Aktionen und/oder Manipulationen durchzuführen, bevor sie abgeschlossen sind (oder In einigen Fällen wird die Operation insgesamt gestoppt. Dies ist genau das, was wir tun möchten, wenn versucht wird, auf einen nicht vorhandenen Enumerator zu verweisen.

Hier ist ein Beispiel, das das Proxy-Objekt zum Nachahmen von Enums verwendet. Die Enumeratoren in diesem Beispiel sind Standard-HTTP-Methoden (d. H. "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 Stellvertreter?

Ich erinnere mich, als ich anfing, das Word-Proxy überall zu sehen, machte es definitiv lange keinen Sinn für mich. Wenn Sie das gerade jetzt sind, denke ich, eine einfache Möglichkeit, Proxies zu verallgemeinern, besteht darin, sie als Software, Institutionen oder sogar Personen zu verstehen, die als Vermittler oder Zwischenhändler zwischen zwei Servern, Unternehmen oder Personen fungieren. 

15
Govind Rai

Dies 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 Sie definieren Ihre Enummen so:

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

Und so greifen Sie auf Ihr Enum 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 2 Methoden, um Enumerationen von Nachrichtenobjekten abzubilden.

Einige Vorteile dieses Ansatzes:

  • Einfach zu deklarieren
  • Einfach auf Ihre Enums zugreifen
  • Ihre Aufzählungen können komplexe Typen sein
  • Die Enum-Klasse hat einige assoziative Zwischenspeicherungen, wenn Sie häufig mit getByValue arbeiten

Einige Nachteile:

  • Da gibt es einige unordentliche Speicherverwaltung, da ich die Verweise auf die Aufzählung beibehalten habe
  • Noch keine Art Sicherheit
15
Chris

Erstellen Sie ein Objektliteral:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
12
hvdd

Wenn Sie Backbone verwenden, können Sie mit Backbone.Collection die vollständige Enum-Funktionalität (Suche nach ID, Name, benutzerdefinierten Mitgliedern) kostenlos erhalten.

// 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()
11
Yaroslav

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
8
Xeltor

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
7
David Miró

IE8 unterstützt keine Freeze () -Methode.
Quelle: http://kangax.github.io/compat-table/es5/ , Klicken Sie auf "obsolete Browser anzeigen?". oben, und überprüfen Sie IE8 und kreuzen Sie die Schnittmenge der Zeile.

In meinem aktuellen Spielprojekt habe ich unten verwendet, da wenige Kunden noch 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'
};

Letzteres scheint am effizientesten für string. Es reduziert die Gesamtbandbreite, wenn Server und Client diese Daten austauschen.
Natürlich ist es jetzt Ihre Pflicht, sicherzustellen, dass keine Datenkonflikte vorliegen (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
}
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

Sie müssen nicht sicherstellen, dass Sie unterschiedlichen Enumerationswerten auf diese Weise doppelte Nummern zuweisen. Ein neues Objekt wird instanziiert und allen Aufzählungswerten zugewiesen.

4
Shivanshu Goyal

Ich habe mir einen this Ansatz ausgedacht, der nach enums in Java modelliert ist. Diese sind typsicher, sodass Sie auch instanceof-Prüfungen durchführen können.

Sie können die Aufzählung folgendermaßen definieren:

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

Days bezieht sich jetzt auf die Days-Enumeration:

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
    }

})();
4
Vivin Paliath

Ich habe gerade ein NPM-Paket veröffentlicht gen_enum ermöglicht es Ihnen, schnell eine Enum-Datenstruktur in Javascript zu 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 nette Sache an diesem kleinen Tool ist, dass in einer modernen Umgebung (einschließlich Nodejs und IE 9+ Browser)) das zurückgegebene Enum-Objekt unveränderlich ist.

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

Updates

Ich veraltete gen_enum Paket und füge die Funktion in constjs Paket ein, das mehr Funktionen bietet, einschließlich unveränderlicher Objekte, JSON-String-Deserialisierung, String-Konstanten und Bitmap-Generierung usw. Checkout https: //www.npmjs. com/package/constjs für weitere Informationen

So aktualisieren Sie von gen_enum zu constjs ändern Sie einfach die Anweisung

var genEnum = require('gen_enum');

zu

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

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 wie folgt 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 für verschiedene Namen derselbe Wert verwendet wird):

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 die HTML-Auswahloptionen auch problemlos abrufen:

var html = enum1.GetSelectOptionsHTML();

Was gilt:

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

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

4
LNT

Auch wenn nur statische Methoden (und keine statischen Eigenschaften) in ES2015 unterstützt werden (siehe hier sowie §15.2.2.2), können Sie seltsamerweise das Folgende mit Babel mit der Voreinstellung es2015 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 habe festgestellt, dass dies erwartungsgemäß auch über Module hinweg funktioniert (z. B. Importieren der CellState-Enumeration aus einem anderen Modul) und auch beim Importieren eines Moduls mit Webpack.

Der Vorteil dieser Methode gegenüber den meisten anderen Antworten ist, dass Sie sie neben einem statischen Typprüfer verwenden können (z. B. Flow ). Sie können während der Entwicklungszeit mithilfe der statischen Typprüfung festlegen, dass Ihre Variablen, Parameter usw. sind von der spezifischen CellState "enum" und nicht von irgendeiner anderen enum (die sich bei Verwendung generischer Objekte oder Symbole nicht unterscheiden ließe).

aktualisieren

Der obige Code hat den Nachteil, dass damit zusätzliche Objekte vom Typ CellState erstellt werden können (obwohl sie nicht den statischen Feldern von CellState zugewiesen werden können, da sie eingefroren sind). Der unten beschriebene Code bietet jedoch folgende Vorteile:

  1. es dürfen keine weiteren Objekte vom Typ CellState angelegt werden
  2. sie sind garantiert, dass keine zwei Enum-Instanzen denselben Code erhalten
  3. dienstprogrammmethode, um das Enum von einer Zeichenfolgendarstellung zurückzuholen
  4. die values-Funktion, die alle Instanzen der Enumeration zurückgibt, muss den Rückgabewert nicht auf die obige 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;
    

Zum Zeitpunkt des Schreibens Oktober 2014 - hier ist eine zeitgemäße Lösung. Ich schreibe die Lösung als Knotenmodul und habe einen Test unter Verwendung von Mocha und Chai sowie UnterstrichJS enthalten. Sie können diese einfach ignorieren und den Enum-Code verwenden, wenn Sie dies bevorzugen.

Ich habe viele Einträge mit übermäßig verschachtelten Bibliotheken usw. gesehen. Die Lösung für die Unterstützung von Enumerationen in Javascript 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, erhalten Sie eine Enum-Factory. Sie können alle Schlüssel erhalten, indem Sie enum.keys aufrufen, und Sie können die Schlüssel selbst mit Integer-Konstanten abgleichen. Sie können die Factory mit anderen Werten wiederverwenden und die generierten Enums mithilfe des modularen Ansatzes von Node exportieren. 

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

3
arcseldon

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"
3
user2254487

Hier sind ein paar verschiedene Möglichkeiten, TypeScript-Enums zu implementieren.

Der einfachste Weg besteht darin, einfach ein Objekt zu durchlaufen und dem Objekt invertierte Schlüssel-Wert-Paare hinzuzufügen. Der einzige Nachteil ist, dass Sie den Wert für jedes Element 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 ein Enum mit einem String zu erstellen. Während diese Version etwas komplizierter ist, erledigt 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
3
Blake Bowen

So übersetzt TypeScript seine enum in 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]'Active' zurückgibt, aber dann wurde mir klar, dass sein unkomplizierter - Assignment-Operator Wert zuordnet und ihn dann zurückgibt:

obj['foo'] = 1
// => 1

einfachste Lösung:

Erstellen

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

Wert erhalten

console.log(Status.Ready) // 1

Holen Sie sich den Schlüssel

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

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

var Helper = {
    isEmpty: function (obj) {
        return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
    },

    isObject: function (obj) {
        return (typeof obj === 'object');
    },

    sortObjectKeys: function (object) {
        return Object.keys(object)
            .sort(function (a, b) {
                c = a - b;
                return c
            });
    },
    containsItem: function (arr, item) {
        if (arr && Array.isArray(arr)) {
            return arr.indexOf(item) > -1;
        } else {
            return arr === item;
        }
    },

    pushArray: function (arr1, arr2) {
        if (arr1 && arr2 && Array.isArray(arr1)) {
            arr1.Push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
        }
    }
};
function TypeHelper() {
    var _types = arguments[0],
        _defTypeIndex = 0,
        _currentType,
        _value,
        _allKeys = Helper.sortObjectKeys(_types);

    if (arguments.length == 2) {
        _defTypeIndex = arguments[1];
    }

    Object.defineProperties(this, {
        Key: {
            get: function () {
                return _currentType;
            },
            set: function (val) {
                _currentType.setType(val, true);
            },
            enumerable: true
        },
        Value: {
            get: function () {
                return _types[_currentType];
            },
            set: function (val) {
                _value.setType(val, false);
            },
            enumerable: true
        }
    });
    this.getAsList = function (keys) {
        var list = [];
        _allKeys.forEach(function (key, idx, array) {
            if (key && _types[key]) {

                if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                    var json = {};
                    json.Key = key;
                    json.Value = _types[key];
                    Helper.pushArray(list, json);
                }
            }
        });
        return list;
    };

    this.setType = function (value, isKey) {
        if (!Helper.isEmpty(value)) {
            Object.keys(_types).forEach(function (key, idx, array) {
                if (Helper.isObject(value)) {
                    if (value && value.Key == key) {
                        _currentType = key;
                    }
                } else if (isKey) {
                    if (value && value.toString() == key.toString()) {
                        _currentType = key;
                    }
                } else if (value && value.toString() == _types[key]) {
                    _currentType = key;
                }
            });
        } else {
            this.setDefaultType();
        }
        return isKey ? _types[_currentType] : _currentType;
    };

    this.setTypeByIndex = function (index) {
        for (var i = 0; i < _allKeys.length; i++) {
            if (index === i) {
                _currentType = _allKeys[index];
                break;
            }
        }
    };

    this.setDefaultType = function () {
        this.setTypeByIndex(_defTypeIndex);
    };

    this.setDefaultType();
}

var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var enumA = new TypeHelper(TypeA, 4);

document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setType("200L", false);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

enumA.setDefaultType();
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setTypeByIndex(1);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

document.writeln("is equals = ", (enumA.Value == TypeA["2"]));

3

Sie können dies versuchen:

   var Enum = Object.freeze({
            Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
            Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
            });

    alert(Enum.Role.Supervisor);
    alert(Enum.Color.GREEN);
    var currentColor=0;
    if(currentColor == Enum.Color.RED) {
       alert('Its Red');
    }
2
Muhammad Awais

es7 Weg, (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(', ') }

}
2

Ich hatte es vor einiger Zeit mit einer Mischung aus __defineGetter__ und __defineSetter__ oder defineProperty getan, abhängig von der JS-Version.

Hier ist die Enumerierungsfunktion, die ich erstellt habe: https://Gist.github.com/gfarrell/6716853

Du würdest es so benutzen:

var Colours = Enum('RED', 'GREEN', 'BLUE');

Und es würde eine unveränderliche Zeichenfolge erstellen: int dictionary (eine Aufzählung).

2
GTF

Ich habe enumerationjs eine very tiny Bibliothek geschrieben, um das Problem zu lösen die die Typensicherheit gewährleistet, Enum-Konstanten erlaubt, von einem Prototyp zu erben, garantiert, dass Enum-Konstanten und Enum-Typen unveränderlich sind + viele kleine Funktionen. Es erlaubt eine Menge Code umzuwandeln und eine gewisse Logik innerhalb der Enumendefinition zu verschieben. Hier ist ein Beispiel : 

var CloseEventCodes = new Enumeration("closeEventCodes", {
  CLOSE_NORMAL:          { _id: 1000, info: "Connection closed normally" },
  CLOSE_GOING_AWAY:      { _id: 1001, info: "Connection closed going away" },
  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: "Connection closed due to protocol error"  },
  CLOSE_UNSUPPORTED:     { _id: 1003, info: "Connection closed due to unsupported operation" },
  CLOSE_NO_STATUS:       { _id: 1005, info: "Connection closed with no status" },
  CLOSE_ABNORMAL:        { _id: 1006, info: "Connection closed abnormally" },
  CLOSE_TOO_LARGE:       { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
    console.log(this.info); 
  }
});


CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true

Enumeration ist im Grunde eine Fabrik. 

Vollständig dokumentierte Anleitung hier verfügbar. Hoffe das hilft. 

2
Jules Randolph

Wirklich wie das, was @Duncan oben gemacht hat, aber ich mag es nicht, den globalen Objektfunktionsraum mit Enum zu belegen, also habe ich folgendes geschrieben:

function mkenum_1()
{
  var o = new Object();
  var c = -1;
  var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};

  for (i in arguments) {
    var e = arguments[i];
    if ((!!e) & (e.constructor == Object))
      for (j in e)
        f(j, (c=e[j]));
    else
      f(e, ++c);
    }

  return Object.freeze ? Object.freeze(o) : o;
}

var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');

console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);

// Output is:
// MED := 1
// LRG := 100

@Stijin hat auch eine nette Lösung (bezogen auf seinen Blog), die Eigenschaften dieser Objekte enthält. Auch dafür habe ich einen Code geschrieben, den ich als nächstes einbeziehe.

function mkenum_2(seed)
{
    var p = {};

    console.log("Seed := " + seed);

    for (k in seed) {
        var v = seed[k];

        if (v instanceof Array)
            p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
        else
            p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
    }
    seed.properties = p;

    return Object.freeze ? Object.freeze(seed) : seed;
}

Diese Version erstellt eine zusätzliche Eigenschaftsliste, die die Konvertierung von Kurznamen und Kurzcodes ermöglicht. Ich mag diese Version, weil man die Dateneingabe in den Eigenschaften nicht duplizieren muss, da der Code dies für Sie tut.

var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });

Diese beiden können zu einer einzigen Verarbeitungseinheit, dem Mkenum, kombiniert werden (Aufzählungen verwenden, Werte zuweisen, Eigenschaftsliste erstellen und hinzufügen). Da ich aber heute schon viel zu viel Zeit damit verbracht habe, werde ich die Kombination dem lieben Leser als Übung überlassen.

1
Andrew Philips

Sie können Object.prototype.hasOwnProperty () verwenden. 

var findInEnum,
    colorEnum = {
    red : 0,
    green : 1,
    blue : 2
};

// later on

findInEnum = function (enumKey) {
  if (colorEnum.hasOwnProperty(enumKey)) {
    return enumKey+' Value: ' + colorEnum[enumKey]
  }
}

alert(findInEnum("blue"))

1
Gildas.Tambo

Was ist ein Enum in my opinion: Es ist ein unveränderliches Objekt, auf das immer zugegriffen werden kann, und Sie können Elemente miteinander vergleichen. Die Elemente haben jedoch gemeinsame Eigenschaften/Methoden. Die Objekte selbst oder die Werte können jedoch nicht geändert werden nur einmal instanziiert.

Aufzählungen werden verwendet, um Datentypen, Einstellungen und Aktionen zu vergleichen, mit denen solche Dinge angenommen/beantwortet werden. 

Daher benötigen Sie Objekte mit derselben Instanz, damit Sie prüfen können, ob es sich um einen Aufzählungstyp if(something instanceof enum).__ handelt. Auch wenn Sie ein Aufzählungsobjekt erhalten, mit dem Sie etwas erledigen können, unabhängig vom Aufzählungstyp, sollte dies der Fall sein reagieren Sie immer auf dieselbe Weise.

In meinem Fall ist es ein Vergleich der Werte von Datentypen. Es kann jedoch alles sein, von Blöcken in 3D-Richtung in eine 3D-Spielrichtung zu ändern oder Werte an eine bestimmte Objekttypregistrierung weiterzuleiten.

Wenn man bedenkt, dass es sich um Javascript handelt und es keinen festen Aufzählungstyp gibt, wird am Ende immer eine eigene Implementierung erstellt, und da dieser Thread zeigt, gibt es Legionen von Implementierungen, ohne dass eine davon als falsch gilt.


Das ist was ich für Enums verwende. Da Aufzählungen unveränderlich sind (oder zumindest heh sein sollten), friere ich die Objekte ein, so dass sie nicht leicht manipuliert werden können.

Die Enums können von EnumField.STRING verwendet werden. Sie verfügen über eigene Methoden, die mit ihren Typen arbeiten. Um zu testen, ob etwas an ein Objekt übergeben wurde, können Sie if(somevar instanceof EnumFieldSegment) verwenden.

Es ist vielleicht nicht die eleganteste Lösung und ich bin offen für Verbesserungen, aber diese Art von unveränderlichen Enummen (sofern Sie sie nicht einfrieren) ist genau die Anwendung, die ich brauche.

Ich erkenne auch, dass ich den Prototyp mit einem {} überschreiben konnte, aber mein Verstand arbeitet besser mit diesem Format ;-).

/**
 * simple parameter object instantiator
 * @param name
 * @param value
 * @returns
 */
function p(name,value) {
    this.name = name;
    this.value = value;
    return Object.freeze(this);
}
/**
 * EnumFieldSegmentBase
 */
function EnumFieldSegmentBase() {
    this.fieldType = "STRING";
}
function dummyregex() {
}
dummyregex.prototype.test = function(str) {
    if(this.fieldType === "STRING") {
        maxlength = arguments[1];
        return str.length <= maxlength;
    }
    return true;
};

dummyregexposer = new dummyregex();
EnumFieldSegmentBase.prototype.getInputRegex = function() { 
    switch(this.fieldType) {
        case "STRING" :     return dummyregexposer;  
        case "INT":         return /^(\d+)?$/;
        case "DECIMAL2":    return /^\d+(\.\d{1,2}|\d+|\.)?$/;
        case "DECIMAL8":    return /^\d+(\.\d{1,8}|\d+|\.)?$/;
        // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
        case "BOOLEAN":     return dummyregexposer;
    }
};
EnumFieldSegmentBase.prototype.convertToType = function($input) {
    var val = $input;
    switch(this.fieldType) {
        case "STRING" :         val = $input;break;
        case "INT":         val==""? val=0 :val = parseInt($input);break;
        case "DECIMAL2":    if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break;
        case "DECIMAL8":    if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break;
        // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
        case "BOOLEAN":     val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf()))  ;break;
    }
    return val;
};
EnumFieldSegmentBase.prototype.convertToString = function($input) {
    var val = $input;
    switch(this.fieldType) {
        case "STRING":      val = $input;break;
        case "INT":         val = $input+"";break;
        case "DECIMAL2":    val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break;
        case "DECIMAL8":    val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break;
        case "BOOLEAN":     val = $input ? "true" : "false"  ;break;
    }
    return val;
};
EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) {
    var val = false;
    switch(this.fieldType) {
        case "STRING":      val = ($val1===$val2);break;
        case "INT":         val = ($val1===$val2);break;
        case "DECIMAL2":    val = ($val1.comparedTo($val2)===0);break;
        case "DECIMAL8":    val = ($val1.comparedTo($val2)===0);break;
        case "BOOLEAN":     val = ($val1===$val2);break;
    }
    return val;
};

/**
 * EnumFieldSegment is an individual segment in the 
 * EnumField
 * @param $array An array consisting of object p
 */
function EnumFieldSegment() {
    for(c=0;c<arguments.length;c++) {
        if(arguments[c] instanceof p) {
            this[arguments[c].name] = arguments[c].value;
        }
    }
    return Object.freeze(this); 
}
EnumFieldSegment.prototype = new EnumFieldSegmentBase();
EnumFieldSegment.prototype.constructor = EnumFieldSegment;


/**
 * Simple enum to show what type of variable a Field type is.
 * @param STRING
 * @param INT
 * @param DECIMAL2
 * @param DECIMAL8
 * @param BOOLEAN
 * 
 */
EnumField = Object.freeze({STRING:      new EnumFieldSegment(new p("fieldType","STRING")), 
                            INT:        new EnumFieldSegment(new p("fieldType","INT")), 
                            DECIMAL2:   new EnumFieldSegment(new p("fieldType","DECIMAL2")), 
                            DECIMAL8:   new EnumFieldSegment(new p("fieldType","DECIMAL8")), 
                            BOOLEAN:    new EnumFieldSegment(new p("fieldType","BOOLEAN"))});
1
Tschallacka
class Enum {
  constructor (...vals) {
    vals.forEach( (val, i) => {
      Object.defineProperty(this, val.toUpperCase(), {
        get () {
          return i;
        },
        set (val) {
          const enum_val = "CONSTANT";
          // generate TypeError associated with attempting to change the value of a constant
          enum_val = val;
        }
      });
    });
  }
}

Verwendungsbeispiel:

const COLORS = new Enum("red", "blue", "green")
console.log(COLORS.RED) // 0
console.log(COLORS.GREEN) // 2
1
papiro

Lesen Sie alle Antworten und haben keine non-verbose und DRY Lösung gefunden. Ich benutze diesen Einzeiler:

const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});

es erzeugt ein Objekt mit lesbaren Werten:

{
  DRAW: 'DRAW',
  SCALE: 'SCALE',
  DRAG: 'DRAG'
}
0
oluckyman

Die Alien-Lösung besteht darin, die Dinge so einfach wie möglich zu machen:

  1. verwenden Sie das Enum-Schlüsselwort (in Javascript reserviert).
  2. Wenn das enum-Schlüsselwort nur reserviert, aber nicht in Ihrem Javascript implementiert ist, definieren Sie Folgendes

    const enumerate = spec => spec.split(/\s*,\s*/)
      .reduce((e, n) => Object.assign(e,{[n]:n}), {}) 
    

Jetzt können Sie es problemlos verwenden

const kwords = enumerate("begin,end, procedure,if")
console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)

Ich sehe keinen Grund, die Aufzählungswerte explizite Variablen zu machen. Die Skripte sind sowieso morphisch und es spielt keine Rolle, ob ein Teil Ihres Codes ein String oder ein gültiger Code ist. Was wirklich zählt, ist, dass Sie sich bei jeder Verwendung oder Definition nicht mit Tonnen von Anführungszeichen befassen müssen. 

0
Little Alien
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });

Sie müssen kein id angeben, Sie können nur ein leeres Objekt verwenden, um die Enumerationen zu vergleichen. 

if (incommingEnum === DaysEnum.monday) //incommingEnum is monday

EDIT: Wenn Sie das Objekt serialisieren (z. B. in JSON), werden Sie wieder id.

0
Pylinux

Diese Antwort ist ein alternativer Ansatz für bestimmte Umstände. Ich brauchte eine Reihe von Bitmasken-Konstanten, die auf Attributunterwerten basieren (Fälle, in denen ein Attributwert ein Array oder eine Liste von Werten ist). Es umfasst das Äquivalent mehrerer überlappender Enumerationen. 

Ich habe eine Klasse erstellt, um die Bitmaskenwerte zu speichern und zu generieren. Ich kann dann die Pseudo-Konstanten-Bitmaskenwerte auf diese Weise verwenden, um beispielsweise zu testen, ob Grün in einem RGB-Wert vorhanden ist: 

if (value & Ez.G) {...}

In meinem Code erstelle ich nur eine Instanz dieser Klasse. Es scheint keinen sauberen Weg zu geben, ohne mindestens eine Instanz der Klasse zu instanziieren. Hier ist der Klassendeklarations- und Bitmaskenwert-Erzeugungscode:

class Ez {
constructor() {
    let rgba = ["R", "G", "B", "A"];
    let rgbm = rgba.slice();
    rgbm.Push("M");              // for feColorMatrix values attribute
    this.createValues(rgba);
    this.createValues(["H", "S", "L"]);
    this.createValues([rgba, rgbm]);
    this.createValues([attX, attY, attW, attH]);
}
createValues(a) {                // a for array
    let i, j;
    if (isA(a[0])) {             // max 2 dimensions
        let k = 1;
        for (i of a[0]) {
            for (j of a[1]) {
                this[i + j] = k;
                k *= 2;
            }
        }
    }
    else {                       // 1D array is simple loop
        for (i = 0, j = 1; i < a.length; i++, j *= 2)
            this[a[i]] = j;
   }
}

Das 2D-Array ist für das SVG-FeColorMatrix-Werte-Attribut, bei dem es sich um eine 4x5-Matrix aus RGBA von RGBAM handelt. Die resultierenden Ez-Eigenschaften sind Ez.RR, Ez.RG usw.

0
jamess

Seit ES6 können Sie keine Enums per se erstellen, haben jedoch eine elegantere Syntax für die automatische Codevervollständigung, zumindest in VSCode:

class MyEnum {
  const A = '1'
  const B = '2'
  const C = '3'
}

Auf der positiven Seite können Sie wie in den anderen Antworten alles, was Sie möchten, in die Variable const einfügen. In Node können Sie es auch als Teil eines Moduls exportieren, wobei ein aussagekräftiger Name erhalten bleibt.

import { MyEnum } from './my-enum'

console.log(MyEnum.B)

Hoffe das hilft.

0
CodingNagger

Sie können eine einfache Funktion zum Invertieren von Schlüsseln und Werten verwenden. Sie funktioniert auch mit Arrays, da numerische Ganzzahlzeichenfolgen in Zahlen umgewandelt werden. Der Code ist klein, einfach und für diesen und andere Anwendungsfälle wiederverwendbar.

var objInvert = function (obj) {
    var invert = {}
    for (var i in obj) {
      if (i.match(/^\d+$/)) i = parseInt(i,10)
      invert[obj[i]] = i
    }
    return invert
}
 
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL',
'BOSSA-NOVA','POP','INDIE']))

console.log(musicStyles)

0
David Lemon