Qual è la sintassi preferita per la definizione di enum in JavaScript?


2082

Qual è la sintassi preferita per la definizione di enum in JavaScript? Qualcosa di simile a:

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

// later on

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

O c'è un linguaggio più preferibile?


132
Non utilizzare 0come numero di enumerazione. A meno che non sia usato per qualcosa che non è stato impostato. JS considera false || undefined || null || 0 || "" || '' || NaNtutti lo stesso valore rispetto all'utilizzo ==.
Matsko,

153
@matsko non è solo un argomento contro l'utilizzo di ==?
sdm350,

6
0 == nullrestituisce false
mcont,

11
Ma false == 0e +null == 0(e le conversioni ai numeri avvengono a volte quando non te lo aspetti), null == undefinedanche se, ed +undefinedè NaN(però NaN != NaN).
sanderd17,

46
La matrice della doppia uguaglianza è più confusa della formattazione automatica di microsoft word
aaaaaa

Risposte:


896

Da 1.8.5 è possibile sigillare e congelare l'oggetto, quindi definire quanto sopra come:

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

o

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

e voilà! JS enums.

Tuttavia, ciò non impedisce di assegnare un valore indesiderato a una variabile, che è spesso l'obiettivo principale di enum:

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

Un modo per garantire un maggior grado di sicurezza dei tipi (con enumerazioni o altro) è utilizzare uno strumento come TypeScript o Flow .

fonte

Le citazioni non sono necessarie ma le ho conservate per coerenza.


6
Secondo Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ) è applicabile a Firefox 4, IE 9, Opera 11.60 e so che funziona in Chrome.
Artur Czajka,

77
Questa è la risposta in questo momento nel 2012. Più semplice: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Non è necessario specificare un ID, puoi semplicemente usare un oggetto vuoto per confrontare gli enum. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas,

34
Per compatibilità con le versioni precedenti,if (Object.freeze) { Object.freeze(DaysEnum); }
saluce il

17
Vorrei sottolineare che fare ({ monday: {}, ecc. Significa che se converti quell'oggetto in JSON tramite stringify otterrai [{"day": {}}]che non funzionerà.
jcollum,

10
@Supuhstar La mia opinione su questa domanda ora è diversa. Non usare freeze (), è completamente inutile e fa perdere tempo a fare cose "stupide". Se si desidera esporre un enum, è sufficiente esporre questo: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Il confronto di oggetti come nel mio commento precedente è MOLTO PIÙ LENTO rispetto al confronto di numeri.
Gabriel Llamas,

608

Questa non è una gran risposta, ma direi che funziona bene, personalmente

Detto questo, dal momento che non importa quali siano i valori (hai usato 0, 1, 2), userei una stringa significativa nel caso in cui avessi mai voluto produrre il valore corrente.


377
Lo ha affermato in un'altra risposta, ma poiché questa è la risposta accettata, la posterò qui. La soluzione del PO è corretta. Sarà ancora meglio, tuttavia, se utilizzato con Object.freeze(). Ciò impedirà ad altri codici di modificare i valori dell'enum. Esempio:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth,

5
@TolgaE grazie per quella biblioteca! Mi ha ispirato non solo a ridurlo al minimo indispensabile, ma anche aggiungere un paio di funzionalità! Ho biforcuto il tuo e ho messo tutto qui: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar

3
@Supuhstar È fantastico! Sono contento che tu possa usarlo .. Sentiti libero di fare una richiesta pull se vuoi che sia unita in questa libreria, quindi posso aggiornare la libreria npm
Tolga E

2
Se qualcuno è interessato, ho implementato enumerazioni sicure di tipo simili a come sono in Java. Questo significa che puoi fare instanceofcontrolli. Ad esempio ColorEnum.RED instanceof ColorEnum(ritorna true). Puoi anche risolvere un'istanza per nome ColorEnum.fromName("RED") === ColorEnum.RED(restituisce true). Ogni istanza ha anche un .name()e un .ordinal()metodo e l'enum stesso ha un values()metodo che restituisce un array di tutte le costanti.
Vivin Paliath,

3
Non sono sicuro di essere d'accordo con il suggerimento "stringa significativa". Gli enum non dovrebbero essere pensati come stringhe o numeri; sono tipi di dati astratti. Non dovrebbe essere possibile "produrre il valore corrente" senza un metodo di supporto. In Java e .NET, è il ToString()metodo. Noi sviluppatori di JS facciamo già troppo affidamento sulle cose "solo funzionanti"! Inoltre, si dovrebbe essere in grado di fare rapidamente switchun enum. Il confronto delle stringhe è più lento rispetto al confronto dei numeri, quindi otterrai switchprestazioni leggermente peggiori se usi stringhe anziché numeri interi.
Rabadash8820,

501

AGGIORNARE

Grazie per tutti i voti a tutti, ma non credo che la mia risposta qui sotto sia il modo migliore per scrivere enumerazioni in JavaScript. Vedi il mio post sul blog per maggiori dettagli: Enums in JavaScript .


Avvisare il nome è già possibile:

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

In alternativa, puoi creare oggetti valori, così puoi avere la torta e mangiarla anche tu:

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, poiché è un linguaggio dinamico, è anche possibile aggiungere valori enum all'insieme in un secondo momento:

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

Ricorda, i campi dell'enum (valore, nome e codice in questo esempio) non sono necessari per il controllo dell'identità e sono lì solo per comodità. Inoltre, il nome della proprietà size non deve essere codificato, ma può anche essere impostato in modo dinamico. Quindi supponendo che tu conosca solo il nome per il tuo nuovo valore enum, puoi ancora aggiungerlo senza problemi:

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

Ovviamente ciò significa che non è più possibile assumere alcune ipotesi (ad esempio, quel valore rappresenta l'ordine corretto per la dimensione).

Ricorda, in JavaScript un oggetto è proprio come una mappa o una tabella hash . Un insieme di coppie nome-valore. Puoi collegarli o manipolarli in altro modo senza sapere molto su di loro in anticipo.

Esempio

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

E a proposito, se sei interessato agli spazi dei nomi, potresti voler dare un'occhiata alla mia soluzione per la gestione semplice ma potente dello spazio dei nomi e delle dipendenze per JavaScript: Pacchetti JS


quindi come andresti a creare semplicemente una DIMENSIONE se tu avessi solo il suo nome?
Johanisma,

2
@Johanisma: questo caso d'uso non ha davvero senso per gli enum poiché l'idea di essi è che conosci tutti i valori in anticipo. Tuttavia non c'è nulla che ti impedisca di aggiungere valori aggiuntivi in ​​seguito in Javascript. Aggiungerò un esempio di ciò alla mia risposta.
Stijn de Witt,

2
+1 per il link al tuo post con l'approccio delle proprietà. Elegante in quanto le dichiarazioni di base sono semplici, come nel PO, con funzionalità di proprietà aggiunte quando desiderato.
addio,

@Stijin, mi è piaciuta molto la tua soluzione aggiornata. Codice pubblicato nei commenti sul tuo blog e come commento di seguito. Fondamentalmente, usando una funzione, esegui la compilazione delle proprietà da un elenco di hash esistente e facoltativamente congelalo (mkenum_2 nel mio elenco). Saluti.
Andrew Philips,

C'è anche una biblioteca che la implementa, includendo anche belle funzionalità per il confronto e la ricerca inversa: github.com/adrai/enum
Roman M

83

In conclusione: non puoi.

Puoi simularlo, ma non otterrai la sicurezza del tipo. In genere questo viene fatto creando un semplice dizionario di valori stringa associati a valori interi. Per esempio:

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

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

Il problema con questo approccio? È possibile ridefinire accidentalmente l'enumeratore o avere accidentalmente valori enumeranti duplicati. Per esempio:

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

modificare

Che dire dell'Object.freeze di Artur Czajka? Non avrebbe funzionato per impedirti di andare dal lunedì al giovedì? - Fry Quad

Assolutamente, Object.freezerisolverebbe totalmente il problema di cui mi sono lamentato. Vorrei ricordare a tutti che quando ho scritto quanto sopra, Object.freezenon esisteva davvero.

Ora .... ora apre alcune possibilità molto interessanti.

Modifica 2
Ecco un'ottima libreria per la creazione di enumerazioni.

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

Anche se probabilmente non si adatta a ogni uso valido di enumerazioni, va molto lontano.


103
c'è sicurezza di tipo in javascript?
Scott Evernden,

3
Quindi non mappare i valori sulle proprietà degli oggetti. Usa getter per accedere all'enumeratore (memorizzato come proprietà, per esempio, di un oggetto "privato"). Un'implementazione ingenua sembrerebbe -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax il

2
@Scott Evernden: punto preso. @kangax: il punto è che è ancora un trucco. Gli enum semplicemente non esistono in Javascript, periodo, fine della storia. Anche lo schema suggerito da Tim Sylvester è ancora un trucco tutt'altro che ideale.
Randolpho,

2
Spruzzare il codice con valori letterali non è molto gestibile, quindi ha senso creare costanti per esso. Ovviamente anche Javascript non ha costanti. Quindi in pratica questo è solo un modo per scrivere codice pulito. Non può essere applicato, ma non molto in Javascript. Puoi ridefinire le costanti, le funzioni o quasi tutto. Ad esempio: document.getElementById = function () {alert ("Sei fregato. Javascript non è typesafe.");};
Stijn de Witt,

3
@Randolpho: che dire di Object.freeze di Artur Czajka? Non avrebbe funzionato per impedirti di andare dal lunedì al giovedì?
Michael - Dov'è Clay Shirky il

56

Ecco cosa vogliamo tutti:

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

Ora puoi creare i tuoi enum:

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

In questo modo, è possibile accedere alle costanti come di consueto (Sì. No, Sì, Colore. VERDE) e ottengono un valore int sequenziale (NO = 0, SÌ = 1; ROSSO = 0, VERDE = 1, BLU = 2).

Puoi anche aggiungere metodi, usando Enum.prototype:

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


Modifica - piccolo miglioramento - ora con varargs: (sfortunatamente non funziona correttamente su IE: S ... allora dovrebbe attenersi alla versione precedente)

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

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

Adoro la semplicità di questa risposta!
Marquizzo,

@Marquizzo (e OP) Ho creato una versione migliorata sulla base di questa risposta: stackoverflow.com/a/60309416/1599699
Andrew

53

Nella maggior parte dei browser moderni, esiste un tipo di dati primitivo di simbolo che può essere utilizzato per creare un elenco. Garantirà la sicurezza del tipo dell'enum poiché ogni valore del simbolo è garantito da JavaScript come unico, vale a dire Symbol() != Symbol(). Per esempio:

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

Per semplificare il debug, è possibile aggiungere una descrizione ai valori enum:

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

Demo Plunker

Su GitHub puoi trovare un wrapper che semplifica il codice richiesto per inizializzare l'enum:

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

Questa è la risposta corretta in teoria. In pratica, il supporto del browser 2015 è tutt'altro che sufficiente. La produzione non è di gran lunga pronta.
vbraun,

1
Sebbene il supporto del browser non sia ancora presente, questa è la risposta migliore poiché è vicina a ciò a cui Symbolè destinata.
rvighne,

2
Tuttavia, spesso i valori di enum devono essere serializzabili e i simboli non sono così utili per serializzare e deserializzare.
Andy,

3
Sono solo io o sono Object.freezesolo per le persone che non hanno accettato il fatto che "il monkeypatch a proprio rischio" è il contratto sociale di JS?
Andy,

@Andy yes la serializzazione è fastidiosa. Ho finito per fare un esplicito toJSONsulla classe che contiene di utilizzare questo approccio: stackoverflow.com/questions/58499828/...
Ciro Santilli郝海东冠状病六四事件法轮功

30

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

Passiamo subito al problema: dimensione del file. Ogni altra risposta elencata qui blocca il tuo codice all'estremo. Vi presento che per le migliori prestazioni possibili, la leggibilità del codice, la gestione di progetti su larga scala, il suggerimento di sintassi in molti editor di codice e la riduzione delle dimensioni del codice mediante minificazione, questo è il modo corretto di fare le enumerazioni: variabili di sottolineatura.


wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwv

Variabili di sottolineatura

Come dimostrato nella tabella sopra e nell'esempio seguente, ecco cinque semplici passaggi per iniziare:

  1. Determinare un nome per il gruppo di enumerazione. Pensa a un sostantivo che può descrivere lo scopo dell'enumerazione o almeno le voci dell'enumerazione. Ad esempio, un gruppo di enumerazioni che rappresentano i colori selezionabili dall'utente potrebbe essere meglio chiamato COLORCHOICES che COLORS.
  2. Decidere se le enumerazioni nel gruppo si escludono a vicenda o indipendenti. Se si escludono a vicenda, avviare ciascun nome di variabile enumerato con ENUM_. Se indipendente o affiancato, utilizzareINDEX_ .
  3. Per ogni voce, creare una nuova variabile locale il cui nome inizia con ENUM_oINDEX_ , quindi il nome del gruppo, quindi un carattere di sottolineatura, quindi un nome descrittivo univoco per la proprietà
  4. Aggiungi un ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, o INDEXLEN_(se LEN_o LENGTH_è preferenze personali) variabile enumerato proprio alla fine. Dovresti usare questa variabile ove possibile nel tuo codice per assicurarti che l'aggiunta di una voce extra all'enumerazione e l'incremento di questo valore non rompano il tuo codice.
  5. Dare ad ogni variabile enumerato successivo un valore uno in più l'ultimo, a partire da 0. Non ci sono commenti su questa pagina che diciamo 0non deve essere usato come un valore enumerato perché 0 == null, 0 == false, 0 == "", e altre follie JS. Sottolineo che, per evitare questo problema e aumentare le prestazioni allo stesso tempo, utilizzare sempre ===e non lasciare mai ==apparire nel proprio codice se non con typeof(ex typeof X == "string"). In tutti i miei anni di utilizzo ===, non ho mai avuto problemi con l'utilizzo di 0 come valore di enumerazione. Se sei ancora schizzinoso, 1potrebbe essere usato come valore iniziale nelle ENUM_enumerazioni (ma non nelle INDEX_enumerazioni) senza penalità di prestazione in molti casi.
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

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

Ecco come ricordo quando usare INDEX_e quando usare ENUM_:

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

Tuttavia, ENUM_in determinate circostanze, può essere appropriato come indice, ad esempio quando si contano le occorrenze di ciascun elemento.

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

Ricorda che, nel codice sopra, è davvero facile aggiungere un nuovo tipo di animale domestico: dovrai solo aggiungere una nuova voce dopo ENUM_PET_RATe aggiornare di ENUMLEN_PETconseguenza. Potrebbe essere più difficile e corretto aggiungere una nuova voce in altri sistemi di enumerazione.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwwvwwwwwwwwww

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

Inoltre, questa sintassi delle enumerazioni consente l'estensione della classe chiara e concisa, come mostrato di seguito. Per estendere una classe, aggiungere un numero incrementale alla LEN_voce della classe genitore. Quindi, completare la sottoclasse con la propria LEN_voce in modo che la sottoclasse possa essere estesa ulteriormente in futuro.

Schema di estensione dell'aggiunta

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

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

(Lunghezza: 2.450 byte)

Alcuni potrebbero dire che questo è meno pratico di altre soluzioni: rinuncia a tonnellate di spazio, impiega molto tempo a scrivere e non è ricoperto di sintassi dello zucchero. Quelle persone avrebbero ragione se non minimizzassero il loro codice. Tuttavia, nessuna persona ragionevole lascerebbe un codice non miniato nel prodotto finale. Per questa minificazione, Closure Compiler è il migliore che devo ancora trovare. L'accesso online è disponibile qui . Il compilatore di chiusura è in grado di prendere tutti questi dati di enumerazione e incorporarli, rendendo il tuo Javascript super-piccolo e veloce da eseguire. Pertanto, minimizza con il compilatore di chiusura. Osservare.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwwvwwwwwwwwww

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

Il compilatore di chiusura è in grado di eseguire alcune incredibili ottimizzazioni tramite inferenze che vanno ben oltre le capacità di qualsiasi altro minificatore Javascript. Il compilatore di chiusura è in grado di incorporare variabili primitive impostate su un valore fisso. Closure Compiler è anche in grado di fare inferenze basate su questi valori incorporati ed eliminare blocchi non utilizzati in istruzioni if ​​e loop.

Scrittura del codice tramite il compilatore di chiusura

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

(Lunghezza: 605 byte)

Closure Compiler ti premia per la codifica più intelligente e l'organizzazione del tuo codice perché perché, mentre molti minificatori puniscono il codice organizzato con una dimensione di file minimizzata più grande, Closure Compiler è in grado di setacciare tutta la tua pulizia e sanità mentale per produrre una dimensione di file ancora più piccola se usi i trucchi come le enumerazioni dei nomi delle variabili. Questo, in questa unica mente, è il santo graal della codifica: uno strumento che assiste il tuo codice con una dimensione ridotta più piccola e assiste la tua mente allenando migliori abitudini di programmazione.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwwvwwwwwwwwww

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

Vediamo ora quanto sarebbe grande il file equivalente senza nessuna di queste enumerazioni.

Sorgente senza utilizzo delle enumerazioni (lunghezza: 1.973 byte (477 byte più brevi del codice enumerato!))
Minimizzato senza l'utilizzo delle enumerazioni (lunghezza: 843 byte (238 byte in più del codice enumerato ))

Grafico delle dimensioni del codice



Come visto, senza enumerazioni, il codice sorgente è più breve al costo di un codice minimizzato più grande. Non so di te; ma so per certo che non incorporo il codice sorgente nel prodotto finale. Pertanto, questa forma di enumerazioni è di gran lunga superiore in quanto determina dimensioni di file ridotte al minimo.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwwvwwwwwwwwww

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

Un altro vantaggio di questa forma di enumerazione è che può essere utilizzato per gestire facilmente progetti su larga scala senza sacrificare la dimensione del codice minimizzata. Quando si lavora su un grande progetto con molte altre persone, potrebbe essere utile contrassegnare ed etichettare esplicitamente i nomi delle variabili con chi ha creato il codice in modo che l'autore originale del codice possa essere rapidamente identificato per la correzione collaborativa dei bug.

// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED   = 0,
      ENUM_JG_COLORENUM_GREEN = 1,
      ENUM_JG_COLORENUM_BLUE  = 2,
      ENUMLEN_JG_COLORENUM    = 3;

// later on

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

// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED   = 0,
      ENUM_PL_ARRAYTYPE_ISSORTED   = 1,
      ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob Knight
      ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
      ENUMLEN_PL_COLORENUM         = 4;

// later on

if(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

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

Inoltre, questa forma di enumerazione è anche molto più veloce dopo la minificazione. Nelle normali proprietà denominate, il browser deve utilizzare hashmaps per cercare dove si trova la proprietà sull'oggetto. Sebbene i compilatori JIT memorizzino nella cache in modo intelligente questa posizione sull'oggetto, c'è ancora un enorme sovraccarico a causa di casi speciali come l'eliminazione di una proprietà inferiore dall'oggetto.

Tuttavia, con array PACKED_ELEMENTS continui non indicizzati con indice intero , il browser è in grado di saltare gran parte di tale sovraccarico poiché l'indice del valore nell'array interno è già specificato. Sì, secondo lo standard ECMAScript, tutte le proprietà dovrebbero essere trattate come stringhe. Tuttavia, questo aspetto dello standard ECMAScript è molto fuorviante in termini di prestazioni perché tutti i browser hanno ottimizzazioni speciali per gli indici numerici negli array.

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

Confronta il codice sopra con il codice sotto.

/// Arrays, however, are always lightning fast
const INDEX_REFERENCE_COUNT = 0;
const INDEX_REFERENCE_VALUE = 1;
const INDEXLENGTH_REFERENCE = 2;

var ref = [];
ref[INDEX_REFERENCE_COUNT] = 10;
ref[INDEX_REFERENCE_VALUE] = "foobar";

Si potrebbe obiettare al codice con enumerazioni che sembrano essere molto più lunghe del codice con oggetti ordinari, ma l'aspetto può essere ingannevole. È importante ricordare che la dimensione del codice sorgente non è proporzionale alla dimensione dell'output quando si utilizza l'epic Closure Compiler. Osservare.

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

Il codice minimizzato senza enumerazioni è sopra e il codice minimizzato con enumerazioni è sotto.

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

L'esempio sopra dimostra che, oltre ad avere prestazioni superiori, il codice enumerato comporta anche una dimensione di file ridotta di dimensioni minime.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwwvwwwwwwwwww

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

Inoltre, questa ciliegina personale in cima sta usando questa forma di enumerazioni insieme all'editor di testo CodeMirror in modalità Javascript. La modalità di evidenziazione della sintassi Javascript di CodeMirror evidenzia le variabili locali nell'ambito corrente. In questo modo, sai immediatamente quando digiti correttamente un nome di variabile perché se il nome della variabile è stato precedentemente dichiarato con la varparola chiave, il nome della variabile diventa un colore speciale (ciano per impostazione predefinita). Anche se non usi CodeMirror, almeno il browser genera un utile[variable name] is not definedeccezione durante l'esecuzione di codice con nomi di enumerazione errati. Inoltre, strumenti JavaScript come JSLint e Closure Compiler sono molto rumorosi nel dirti quando si digita male in un nome di variabile di enumerazione. CodeMirror, il browser e vari strumenti Javascript messi insieme rendono il debug di questa forma di enumerazione molto semplice e davvero facile.

Dimostrazione di evidenziazione di CodeMirror

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

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

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

Nel frammento di cui sopra, sei stato avvisato con un errore perché ENUM_COLORENUM_DNEnon esiste.


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwwvwwwwwwwwww

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Penso che sia sicuro affermare che questa metodologia di enumerazione è davvero il modo migliore di andare non solo per la dimensione del codice minimizzata, ma anche per le prestazioni, il debug e la collaborazione.

Dopo aver letto una domanda utile, ringrazio l'autore per aver dedicato del tempo alla sua scrittura facendo clic sulla freccia in alto a sinistra in alto nella casella della domanda. Ogni casella di risposta ha anche una di queste frecce verso l'alto.


Eh. Preferisco fortemente la leggibilità, la facilità d'uso e la comprensione delle dimensioni del codice.
Andrew,

1
@Andrew Con la mia risposta, puoi avere entrambi. La mia risposta si traduce nel codice più semplice da usare / gestire e nella dimensione minima del codice minimizzato. 🙂
Jack Giffin,

1
@Andrew Ho provato ad applicare il tuo Yet Another Enum (YEA!) All'esempio del parser di colori nella mia risposta. Tuttavia, ho riscontrato diversi problemi che potresti voler affrontare. YEA non ha modo di estendere le enumerazioni con sottoclassi, costringendomi a creare classi padre e figlio separate, che potrebbe essere abbastanza difficile da gestire su grandi progetti. YEA non garantisce che la voce esista (ex colors.REEDrese undefined), quindi gli errori di battitura creano enigmi sfuggenti. YEA non distingue tra l'uso di enumerazioni come indici e ID, portando a codice confuso in cui tutto sembra uguale. ...
Jack Giffin,

1
@Andrew ... YEA ostacola la capacità di compilazione di Closure Compiler. Confronta il codice sorgente con YEA (3549 byte) con il codice minimizzato con YEA (1344 byte) con il codice minimizzato con la mia soluzione (604 byte). Infine, YEA prevede la "mappatura per nome" perché separa i nomi di stringa dagli ID enumerati. Il mio considera solo l'ID, quindi non è necessaria alcuna "mappatura per nome", che porta a un design più semplice e prestazioni migliori. Grazie per aver condiviso la tua soluzione, ma ha bisogno di molte correzioni prima che possa essere pratico.
Jack Giffin,

1
@Andrew Hai diritto alla tua opinione come io sono alla mia 👍
Jack Giffin,

23

Ho giocato con questo, poiché adoro le mie enumerazioni. =)

Usando Object.definePropertypenso di aver trovato una soluzione in qualche modo praticabile.

Ecco un jsfiddle: http://jsfiddle.net/ZV4A6/

Usando questo metodo .. dovresti (in teoria) essere in grado di chiamare e definire i valori enum per qualsiasi oggetto, senza influenzare altri attributi di quell'oggetto.

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

A causa dell'attributo writable:falsequesto dovrebbe renderlo sicuro.

Quindi dovresti essere in grado di creare un oggetto personalizzato, quindi chiamarlo Enum(). I valori assegnati iniziano da 0 e aumentano per elemento.

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

3
Se aggiungi return this;alla fine di Enum potresti fare:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

Non l'ho considerato, poiché non è il mio normale metodo di fare le cose. Ma hai assolutamente ragione! Lo modificherò in.
Duncan il

Mi piace molto, anche se non sono un grande fan di creare spazio nello spazio degli oggetti (con la funzione globale ENUM). Convertito questo in una funzione mkenum e aggiunto assegnazioni numeriche opzionali => var mixedUp = mkenum ('NERO', {ROSSO: 0x0F00, BLU: 0X0F, VERDE: 0x0F0, BIANCO: 0x0FFF, UNO: 1}, DUE, TRE, QUATTRO) ; // Aggiungendo il mio codice come risposta di seguito. Grazie.
Andrew Philips,

Ad essere sincero, non lo uso nemmeno più. Sto usando il compilatore di chiusura di Google, e questo non funziona troppo bene (o complica solo le cose) se usi l'impostazione Avanzata. Quindi sono appena tornato alla notazione standard degli oggetti.
Duncan,

1
falseè il valore predefinito per writable, enumerablee configurable. Non è necessario masticare le impostazioni predefinite.
ceving il

23

Usa proxy Javascript

TLDR: aggiungi questa classe ai tuoi metodi di utilità e la utilizza in tutto il codice, deride il comportamento di Enum dai linguaggi di programmazione tradizionali e genera effettivamente errori quando provi ad accedere a un enumeratore inesistente o aggiungi / aggiorna un enumeratore. Non c'è bisogno di fare affidamento Object.freeze().

class Enum {
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name];
        }
        throw new Error(`No such enumerator: ${name}`);
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    };

    return new Proxy(enumObj, handler);
  }
}

Quindi crea enum creando un'istanza della classe:

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

Spiegazione completa:

Una caratteristica molto utile di Enum che si ottiene dalle lingue tradizionali è che esplodono (generano un errore di compilazione) se si tenta di accedere a un enumeratore che non esiste.

Oltre a congelare la struttura dell'enum beffardo per impedire l'aggiunta accidentale / maliziosa di valori aggiuntivi, nessuna delle altre risposte affronta quella caratteristica intrinseca di Enums.

Come probabilmente saprai, l'accesso a membri inesistenti in JavaScript restituisce semplicemente undefinede non esplode il tuo codice. Poiché gli enumeratori sono costanti predefinite (ovvero i giorni della settimana), non dovrebbe mai esserci un caso in cui un enumeratore non dovrebbe essere definito.

Non fraintendetemi, il comportamento di JavaScript di ritorno undefinedquando si accede a proprietà non definite è in realtà una funzionalità molto potente del linguaggio, ma non è una funzionalità che si desidera quando si tenta di deridere le strutture Enum tradizionali.

Qui è dove brillano gli oggetti Proxy. I proxy sono stati standardizzati nella lingua con l'introduzione di ES6 (ES2015). Ecco la descrizione da MDN:

L'oggetto Proxy viene utilizzato per definire comportamenti personalizzati per operazioni fondamentali (ad es. Ricerca di proprietà, assegnazione, enumerazione, invocazione di funzioni, ecc.).

Analogamente a un proxy del server Web, i proxy JavaScript sono in grado di intercettare le operazioni sugli oggetti (con l'uso di "trap", chiamandoli hook se preferisci) e consentire di eseguire vari controlli, azioni e / o manipolazioni prima del completamento (o in alcuni casi interrompendo del tutto le operazioni, che è esattamente ciò che vogliamo fare se e quando proviamo a fare riferimento a un enumeratore che non esiste).

Ecco un esempio inventato che utilizza l'oggetto Proxy per imitare Enum. Gli enumeratori in questo esempio sono metodi HTTP standard (ad esempio "GET", "POST", ecc.):

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


A parte: cosa diavolo è un proxy?

Ricordo che quando ho iniziato a vedere la parola proxy ovunque, sicuramente non aveva senso per me per molto tempo. Se sei tu in questo momento, penso che un modo semplice per generalizzare i proxy sia pensarli come software, istituzioni o anche persone che agiscono come intermediari o intermediari tra due server, aziende o persone.


Come fare qualcosa come myEnum.valueOf ("someStringValue")? Previsto: nel caso in cui la stringa di input abbia un valore di un elemento dell'enumeratore, dovrebbe restituire l'elemento. Nel caso in cui nessun articolo abbia quel valore di stringa, genera un'eccezione.
sscarduzio,

@sscarduzio è possibile ignorare il valueOfmetodo predefinito specificandolo come metodo di istanza nella classe Enum. Tuttavia, perché vuoi accedervi in ​​questo modo piuttosto che accedervi tramite la notazione a punti?
Govind Rai,

Il mio enum è const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}) e analizzo inserendo una stringa arbitraria "info" o "debug". Quindi ho bisogno di qualcosa come currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio

1
Perché non puoi semplicemente farlo logLevelEnum[settings.get("log_level")]? l'aggiunta parseOrThrowsarebbe solo ripetitiva a ciò che le trap proxy stanno già facendo per te.
Govind Rai,

17

Questo è un vecchio che conosco, ma il modo in cui è stato implementato da allora tramite l'interfaccia TypeScript è:

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

Ciò consente di cercare su entrambi i MyEnum.Barquali restituisce 1 e MyEnum[1]che restituisce "Bar" indipendentemente dall'ordine di dichiarazione.


1
Inoltre MyEnum ["Bar"] funziona che restituisce 1 ... <3 TypeScript finora ...
David Karlaš

3
e ovviamente se stai effettivamente usando Typescript:enum MyEnum { Foo, Bar, Foobar }
parlamento

16

In ES7 , puoi fare un ENUM elegante basandoti su attributi statici:

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

poi

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

Il vantaggio (dell'uso della classe anziché dell'oggetto letterale) è di avere una classe genitore, Enumquindi tutti i tuoi Enum estenderanno quella classe.

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

4
Potresti spiegare perché avere una classe genitore è un vantaggio, per favore? Sento che mi manca qualcosa!
Jon G,

7
Non farlo. new ColorEnum()non ha assolutamente senso.
Bergi,

3
l'estensione di un enum suona davvero pazza
Codii,

una volta che il linguaggio non lo supporta in modo nativo avrebbe senso mantenere questa convenzione e usarla in questo modo! sono d'accordo!
xpto

Penso che (?) Ciò che OP sta ottenendo è: il vantaggio di statica pura è che è disponibile ovunque come singleton, e non è necessario creare un'istanza della classe - OP non sta suggerendo di farlo! Penso che quello che sta dicendo è che la superclasse Enumha standard di statica metodi di enumerazione su di esso, come getValues(), getNames(), iterate(), ecc Se questo è il caso, non si dispone di reimplementare loro per ogni nuovo tipo di enum.
Ingegnere

15

Questa è la soluzione che uso.

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

E definisci i tuoi enum in questo modo:

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

Ed è così che accedi ai tuoi enum:

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

Di solito uso gli ultimi 2 metodi per mappare gli enum dagli oggetti messaggio.

Alcuni vantaggi di questo approccio:

  • Facile enumerare enums
  • Facile accesso ai tuoi enum
  • I tuoi enumerati possono essere tipi complessi
  • La classe Enum ha un po 'di cache associativa se usi getByValue molto

Alcuni svantaggi:

  • C'è un po 'di gestione della memoria disordinata in corso lì, mentre mantengo i riferimenti agli enum
  • Ancora nessun tipo di sicurezza

14

Crea un oggetto letterale:

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

12
constnon rende immutabili le proprietà dell'oggetto, significa solo che la variabile Modesnon può essere riassegnata a qualcos'altro. Per renderlo più completo, utilizzare a Object.freeze()fianco const.
rvighne,

Si prega di non utilizzare Object.freeze. Impedisce al compilatore di chiusura di incorporare l'oggetto.
Jack Giffin,

11

Se stai usando Backbone , puoi ottenere gratuitamente la funzionalità enum completa (trova per id, nome, membri personalizzati) usando Backbone.Collection .

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

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

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

// using
Colors.Red.print()

8

le tue risposte sono troppo complicate

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

1
@JackGiffin Sono d'accordo che la tua risposta è più performante e che la mia potrebbe richiedere più memoria, anche se non dovresti presumere che tutti vogliano un enum nel modo in cui C ++ l'ha implementata. Si prega di rispettare le altre risposte e gli sviluppatori che potrebbero preferire questa alla tua.
Xeltor,

7

Ho modificato la soluzione di Andre 'Fi':

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

Test:

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

6

Ho escogitato questo approccio che è modellato su enum in Java. Questi sono sicuri per il tipo e quindi è possibile eseguireinstanceof controlli.

Puoi definire enum come questo:

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

Daysora si riferisce Daysall'enum:

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"

L'implemento:

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
    }

})();

Sembra carino, forse dovresti verificare l'esistenza del freezemetodo per la compatibilità con le versioni precedenti? Ad esempio,if (Object.freeze) { Object.freeze(values); }
FBB,

Buon punto! Andrà bene!
Vivin Paliath,

6

IE8 non supporta il metodo freeze ().
Fonte: http://kangax.github.io/compat-table/es5/ , fare clic su "Mostra browser obsoleti?" in alto, e controlla IE8 e blocca l'intersezione di righe della riga.

Nel mio attuale progetto di gioco, ho usato di seguito, poiché pochi clienti usano ancora IE8:

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

Potremmo anche fare:

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

o anche questo:

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

L'ultimo, sembra più efficiente per la stringa, riduce la larghezza di banda totale se il server e il client scambiano questi dati.
Naturalmente, ora è tuo dovere assicurarti che non vi siano conflitti nei dati (RE, EX, ecc. Devono essere univoci, anche 1, 2, ecc. Dovrebbero essere univoci). Si noti che è necessario mantenerli per sempre per la compatibilità con le versioni precedenti.

Incarico:

var wildType = CONST_WILD_TYPES.REGULAR;

confronto:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}

5
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

Non è necessario assicurarsi di non assegnare numeri duplicati a valori enum diversi in questo modo. Un nuovo oggetto viene istanziato e assegnato a tutti i valori enum.


Questa risposta è sottovalutata. È una delle mie idee preferite per la sua semplicità. In pratica, penso che seguirò le stringhe perché è più facile eseguire il debug per ora.
Domino,

Mmm, assicurati solo che questo codice non venga chiamato due volte ...
Andrew,

4

Ecco un paio di modi diversi per implementare enumerazioni TypeScript .

Il modo più semplice è iterare su un oggetto, aggiungendo coppie di valori-chiave invertite all'oggetto. L'unico inconveniente è che è necessario impostare manualmente il valore per ciascun membro.

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


Ed ecco un lodash mixin per creare un enum usando una stringa. Mentre questa versione è un po 'più coinvolta, esegue automaticamente la numerazione per te. Tutti i metodi lodash utilizzati in questo esempio hanno un equivalente JavaScript regolare, quindi puoi facilmente cambiarli se lo desideri.

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

molto intelligente, grazie
Ilan

4

Ho appena pubblicato un pacchetto NPM gen_enum che ti permette di creare rapidamente la struttura di dati Enum in Javascript:

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 

Una cosa bella di questo piccolo strumento è nell'ambiente moderno (inclusi nodejs e browser IE 9+) l'oggetto Enum restituito è immutabile.

Per ulteriori informazioni, consulta https://github.com/greenlaw110/enumjs

aggiornamenti

gen_enumPacchetto obsoleto e unisco la funzione nel pacchetto constjs , che fornisce più funzionalità tra cui oggetti immutabili, deserializzazione di stringhe JSON, costanti di stringhe e generazione di bitmap ecc. Per maggiori informazioni, consultare https://www.npmjs.com/package/constjs

Per eseguire l'aggiornamento da gen_enumal constjssolo modificare l'istruzione

var genEnum = require('gen_enum');

per

var genEnum = require('constjs').enum;

4

La soluzione più semplice:

Creare

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

Ottieni valore

console.log(Status.Ready) // 1

Ottieni chiave

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

4

Ho creato una classe Enum che può recuperare valori E nomi su O (1). Può anche generare una matrice di oggetti contenente tutti i nomi e i valori.

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

Puoi iniziarlo in questo modo:

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

Per recuperare un valore (come Enums in C #):

var val2 = enum1.item2;

Per recuperare un nome per un valore (può essere ambiguo quando si inserisce lo stesso valore per nomi diversi):

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

Per ottenere un array con ciascun nome e valore in un oggetto:

var arr = enum1.GetObjArr();

Genererà:

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

Puoi anche ottenere prontamente le opzioni di selezione html:

var html = enum1.GetSelectOptionsHTML();

Che detiene:

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

4

Anche se in ES2015 sono supportati solo metodi statici (e non proprietà statiche) (vedi anche qui , §15.2.2.2), curiosamente puoi usare il seguito con Babel con il es2015preset:

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

Ho scoperto che funziona come previsto anche su tutti i moduli (ad esempio importando l' CellStateenum da un altro modulo) e anche quando importa un modulo usando Webpack.

Il vantaggio che questo metodo ha sulla maggior parte delle altre risposte è che puoi usarlo insieme a un controllo di tipo statico (ad es. Flow ) e puoi affermare, in fase di sviluppo usando il controllo di tipo statico, che le tue variabili, parametri, ecc. Sono specificiCellState " enum "piuttosto che qualche altro enum (che sarebbe impossibile distinguere se si usassero oggetti o simboli generici).

aggiornare

Il codice sopra ha un difetto in quanto consente di creare oggetti aggiuntivi di tipo CellState(anche se non è possibile assegnarli ai campi statici CellStatepoiché è congelato). Tuttavia, il codice più raffinato di seguito offre i seguenti vantaggi:

  1. non è CellStatepossibile creare più oggetti di tipo
  2. si garantisce che a due istanze enum non sia assegnato lo stesso codice
  3. metodo di utilità per recuperare l'enum da una rappresentazione di stringa
  4. la valuesfunzione che restituisce tutte le istanze dell'enum non deve creare il valore restituito nel modo sopra, manuale (e soggetto a errori).

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

Buon esempio :-)
Ashraf.Shk786

4

es7 way, (iteratore, congelamento), utilizzo:

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'

codice:

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

}

4

Ecco come Typescript traduce enumin Javascript:

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

Adesso:

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

All'inizio ero confuso sul perché obj[1]ritorni 'Active', ma poi ho capito che è semplicemente semplice: l' operatore di assegnazione assegna un valore e poi lo restituisce:

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

4

Puoi fare qualcosa del genere

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

Come definito in questa libreria. https://github.com/webmodule/foo/blob/master/foo.js#L217

Esempio completo https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026


3

Un modo rapido e semplice sarebbe:

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

console.log(Colors.WHITE)  //this prints out "0"

6
La funzione non è necessaria e ti dà esattamente lo stesso risultato di ciò che l'OP ha pubblicato.
Sildoreth,

3

Al momento della stesura, ottobre 2014 , ecco una soluzione contemporanea. Sto scrivendo la soluzione come Node Module e ho incluso un test usando Mocha e Chai, oltre a underscoreJS. Puoi ignorarli facilmente e prendere il codice Enum, se preferisci.

Ho visto molti post con librerie troppo contorte, ecc. La soluzione per ottenere il supporto enum in Javascript è così semplice che non è davvero necessaria. Ecco il codice:

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

E un test per illustrare ciò che ti dà:

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

});

Come puoi vedere, ottieni una factory Enum, puoi ottenere tutte le chiavi semplicemente chiamando enum.keys e puoi abbinare le chiavi stesse a costanti intere. E puoi riutilizzare la fabbrica con valori diversi ed esportare quegli Enum generati usando l'approccio modulare di Node.

Ancora una volta, se sei solo un utente occasionale, o nel browser ecc., Prendi solo la parte di fabbrica del codice, rimuovendo potenzialmente anche la libreria di sottolineatura se non desideri usarlo nel tuo codice.


5
Potresti pubblicare una risposta solo con "ecco come fare come un utente occasionale che vuole solo enumerazioni, non fabbriche, sottolineature o qualcosa di stravagante"?
GreenAsJade

5
Anche se questo è piuttosto fantastico da un punto di vista degli sviluppatori, non è molto pulito o leggibile. La soluzione Enum dall'OP è più semplice e più leggibile in ogni modo, e quindi migliore da usare. Comunque, è davvero incredibile che tu l'abbia inventato.
David,

3

È facile da usare, penso. 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);

AGGIORNARE:

Ci sono i miei codici helper ( TypeHelper).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.