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?
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 .
Zitate werden nicht benötigt, aber ich habe sie aus Gründen der Konsistenz beibehalten.
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.
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
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.
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');
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")});
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
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
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.
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.
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 {/*....*/}
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.
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:
Einige Nachteile:
Erstellen Sie ein Objektliteral:
const Modes = {
DRAGGING: 'drag',
SCALING: 'scale',
CLICKED: 'click'
};
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()
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
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
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.
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
}
})();
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;
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>..."
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
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).
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:
CellState
angelegt werdendie 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.
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"
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
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:
var Status = Object.freeze({
"Connecting":0,
"Ready":1,
"Loading":2,
"Processing": 3
});
console.log(Status.Ready) // 1
console.log(Object.keys(Status)[Status.Ready]) // Ready
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"]));
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');
}
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(', ') }
}
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).
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.
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.
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"))
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"))});
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
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'
}
Die Alien-Lösung besteht darin, die Dinge so einfach wie möglich zu machen:
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.
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.
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.
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.
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)