Koja je preferirana sintaksa za definiranje enums u javascript?

Koja je preferirana sintaksa za definiranje enums u javascript? Nešto poput:

 my.namespace.ColorEnum = { RED : 0, GREEN : 1, BLUE : 2 } // later on if(currentColor == my.namespace.ColorEnum.RED) { // whatever } 

Ili postoji željeni idiom?

1818
13 нояб. postavio David Citron 13. studenog 2008-11-13 22:09 '08 u 22:09 2008-11-13 22:09
@ 46 odgovora
  • 1
  • 2

Budući da je 1.8.5 moguće zatvoriti i zamrznuti objekt, definirati gore navedeno kao:

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

ili

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

i voila! JS oglasi.

Međutim, to vas ne sprječava da dodijelite nepoželjnu vrijednost varijabli, koja je često glavna svrha:

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

Jedan od načina da se osigura viši stupanj sigurnosti tipa (pomoću numeriranja ili na drugi način) je korištenje alata kao što je TypeScript ili Flow .

Izvor

Citati nisu potrebni, ali sam ih sačuvao za dosljednost.

665
18 февр. Odgovor daje Artur Czajka 18. veljače. 2011-02-18 14:03 '11 u 14:03 2011-02-18 14:03

To nije odgovor, ali bih rekao da to radi savršeno, osobno.

border=0

Rekavši to, budući da nije važno koje vrijednosti (upotrijebili ste 0, 1, 2), koristio bih smisleni niz ako ste ikada željeli ispisati trenutnu vrijednost.

592
13 нояб. Odgovor daje Gareth 13. studenog. 2008-11-13 22:13 '08 u 22:13 sati 2008-11-13 22:13

UPDATE . Hvala svima na svemu što mi se sviđa, ali ne mislim da je moj odgovor u nastavku najbolji način za pisanje Javascript oglasa. Pogledajte Moj blog za više detalja: Javascript enumerations .


Upozorenje o naslovu već je moguće:

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

Alternativno, možete stvoriti objekte vrijednosti kako biste mogli dobiti tortu i pojesti je:

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

U Javascriptu, budući da se radi o dinamičkom jeziku, moguće je naknadno dodati skupne vrijednosti nabrajanja:

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

Zapamtite da polja za nabrajanje (vrijednost, ime i kod u ovom primjeru) nisu potrebna za provjeru autentičnosti i dostupna su samo za praktičnost. Isto tako, ime svojstva veličine ne mora biti teško kodirano, ali se može i dinamički postaviti. Pretpostavimo da znate samo ime za novu vrijednost enum, možete ga dodati bez problema:

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

Naravno, to znači da se neke pretpostavke više ne mogu napraviti (na primjer, ova vrijednost predstavlja ispravan redoslijed za veličinu).

Zapamtite da u Javascriptovom objektu izgleda kao karta ili hash-tablica. Skup parova imena i vrijednosti. Možete se pomicati kroz njih ili na drugi način manipulirati njima bez prethodnog poznavanja.

na primjer:

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

A btw, ako vas zanimaju prostori imena, možda ćete htjeti pogledati moje rješenje za jednostavno, ali moćno upravljanje prostorima imena i ovisnosti za javascript: JS pakete

484
05 марта '10 в 1:31 2010-03-05 01:31 Odgovor je dao Stijn de Witt 05. ožujka 2010. u 1:31 AM 2010-03-05 01:31

Zaključak: ne možete.

Možete ga lažirati, ali nećete dobiti vrstu sigurnosti. To se obično radi stvaranjem jednostavnog rječnika vrijednosti niza koje su mapirane u cjelobrojne vrijednosti. Na primjer:

 var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...} Document.Write("Enumerant: " + DaysEnum.tuesday); 

Problem s ovim pristupom? Možete nasumično nadjačati svoj brojač ili nasumice dobiti duplicirane vrijednosti. Na primjer:

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

ispraviti

Što mislite o Arthuru Tsayki Object.freeze? Ne bi li vas to spriječilo od ponedjeljka do četvrtka? - Fry Quad

Apsolutno, Object.freeze potpunosti eliminirati problem na koji sam se žalio. Htio bih podsjetiti sve da kad sam pisao gore, Object.freeze stvarno nije postojao.

Sada ... sada otvara neke vrlo zanimljive mogućnosti.

Uredi 2
Ovdje je vrlo dobra knjižnica za stvaranje popisivanja.

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

Iako to vjerojatno nije prikladno za bilo kakvo valjano korištenje popisa, potrebno je jako dugo vremena.

80
21 авг. odgovor je dao Randolpho 21 aug. 2009-08-21 23:56 '09 u 23:56 2009-08-21 23:56

Ovdje svi želimo:

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

Sada možete izraditi svoje unose:

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

Dakle, konstante se mogu izvršiti na uobičajeni način (YesNo.YES, Color.GREEN), a dobivaju sekvencijalnu vrijednost int (NO = 0, YES = 1; CRVENA = 0, ZELENA = 1, PLAVA = 2).

Također možete dodati metode pomoću Enum.prototype:

 Enum.prototype.values = function() { return this.allValues;  }; 


Uredi - malo poboljšanje - sada uz varargs: (nažalost ne radi ispravno u IE: S ... mora se pridržavati prethodne verzije)

 function Enum() { for (var i in arguments) { this[arguments[i]] = i; } } var YesNo = new Enum('NO', 'YES'); var Color = new Enum('RED', 'GREEN', 'BLUE'); 
51
13 июля '11 в 3:28 2011-07-13 03:28 odgovor je dao Andre 'Fi' 13. srpnja '11 u 3:28 2011-07-13 03:28

U većini suvremenih preglednika postoji tip podataka s primitivnim podacima koji možete koristiti za stvaranje nabrajanja. To će osigurati sigurnost tipova nabrajanja, budući da je svaka vrijednost znaka zajamčena kao JavaScript jedinstvena, to jest, Symbol() != Symbol() . Na primjer:

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

Da biste pojednostavili otklanjanje pogrešaka, popisima možete dodati opise:

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

Plunker demo

Na GitHubu možete pronaći ljusku koja pojednostavljuje kod potreban za inicijalizaciju 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 
45
05 мая '15 в 19:32 2015-05-05 19:32 odgovor je dat Vitalii Fedorenko 05. svibnja u 19:32 2015-05-05 19:32

Igrao sam s ovim jer mi se sviđaju moji oglasi. =)

Koristeći Object.defineProperty mislim da sam smislio nešto održivo rješenje.

Ovdje je jsfiddle: http://jsfiddle.net/ZV4A6/

Koristeći ovu metodu, trebali biste (teoretski) biti u mogućnosti pozvati i definirati vrijednosti nabrajanja za bilo koji objekt bez utjecaja na druge atribute ovog objekta.

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

Zbog atributa za writable:false , to bi ga trebalo učiniti sigurnim.

Tako biste trebali moći stvoriti prilagođeni objekt, a zatim nazvati Enum() na njemu. Vrijednosti dodijeljene vrijednosti počinju od 0 i povećavaju se za svaku stavku.

 var EnumColors={}; EnumColors.Enum('RED','BLUE','GREEN','YELLOW'); EnumColors.RED; // == 0 EnumColors.BLUE; // == 1 EnumColors.GREEN; // == 2 EnumColors.YELLOW; // == 3 
22
21 авг. odgovor je dan Duncanu 21. kolovoza. 2013-08-21 13:34 '13 u 13:34 2013-08-21 13:34

Ovo je staro, što znam, ali način na koji je implementiran putem TypeScript sučelja je:

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

To vam omogućuje da tražite i MyEnum.Bar , koji vraća 1, i MyEnum[1] , koji vraća "Bar" bez obzira na redoslijed oglasa.

18
24 июня '13 в 19:11 2013-06-24 19:11 odgovor je dao Rob Hardy 24. lipnja 13. u 7:11 2013-06-24 19:11

Već je navedena "preferirana sintaksa" većine ljudi. Međutim, postoji zajednički problem: izvedba. Niti jedan od gore navedenih odgovora nije u najmanju ruku vrlo djelotvoran, i svi oni povećavaju veličinu vašeg koda do krajnjih granica. Za realne performanse, jednostavnost čitanja koda i smanjenje doze bez presedana minimiziranjem, to je pravi način za popisivanje.

 const ENUM_COLORENUM_RED = 0, ENUM_COLORENUM_GREEN = 1, ENUM_COLORENUM_BLUE = 2, ENUMLEN_COLORENUM = 3; // later on if(currentColor == ENUM_COLORENUM_RED) { // whatever } 

Osim toga, ova sintaksa omogućuje jasan i koncizan nastavak razreda, kao što je prikazano u nastavku.

(Duljina: 2450 bajtova)

ovdje .  Kompilator Zatvaranje može uzeti sve ove podatke o nabrajanju i ugraditi ih, što čini vaš javascript brzim i super malim.  Obratite pozornost. 

(Duljina: 605 bajtova)

Izvor bez ovih nabrajanja (dužina: 1,973 bajta (477 bajtova kraći!)) 
Dolje bez ovih enumeracija (duljina: 843 bajta (238 bajtova više ))

Kao što možete vidjeti, bez unosa, izvorni kod je kraći zbog većeg smanjenog koda. Ne znam za vas, siguran sam da ne želim uključiti izvorni kod u konačni proizvod, što čini ovaj način unosa puno boljim, jer rezultira manjim veličinama datoteka. Dodajte tome, ovaj oblik unosa je mnogo brži. Doista, ovaj oblik popisivanja je put.

16
15 мая '18 в 19:53 2018-05-15 19:53 odgovorio je Jacku Giffinu 15. svibnja 2010. u 19:53 2018-05-15 19:53

U ES7 možete napraviti elegantan enum koji se oslanja na statičke atribute:

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

 if (currentColor === ColorEnum.GREEN ) {} 

Prednost (korištenjem klase umjesto doslovnog objekta) je imati nadređenu klasu Enum , onda će svi vaši Enums proširiti ovu klasu.

  class ColorEnum extends Enum {} 
15
06 янв. odgovor je dao Abdennour TOUMI siječanj 06 2017-01-06 10:07 '17 u 10:07 2017-01-06 10:07

Ovo je rješenje koje koristim.

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

A svoje prijenose definirate na sljedeći način:

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

Ovako pristupate svojim unosima:

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

Obično koristim posljednje 2 metode za podudaranje enumova iz objekata poruke.

Neke od prednosti ovog pristupa su:

  • Jednostavan za prijavu transfera
  • Jednostavan pristup vašim unosima.
  • Vaši unosi mogu biti složeni.
  • Enum klasa ima neke asocijativne caching ako koristite getByValue puno

Neki nedostaci su:

  • Tamo se događa nešto prljavog upravljanja memorijom, budući da ja spominjem skripte.
  • Još nema sigurnosnog tipa
15
15 мая '12 в 12:26 2012-05-15 12:26 Odgovor je dao Chris 15. svibnja 2012. u 12:26 2012-05-15 12:26

Koristite proxy poslužitelja Javascripta

TL; DR: dodajte ovu klasu u vaše korisne metode i koristite je u svim kodovima, simulira ponašanje Enuma iz tradicionalnih programskih jezika i zapravo daje pogreške kada pokušate pristupiti nepostojećem popisivaču ili dodajte / ažurirajte enumerator. Nema potrebe oslanjati se na Object.freeze() .

 class Enum { constructor(enumObj) { const handler = { get(target, name) { if (target[name]) { 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); } } 

Zatim stvorite enums, stvarajući instancu klase:

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

Potpuno objašnjenje:

Jedna vrlo korisna značajka programa Enums koju dobivate na tradicionalnim jezicima je da eksplodiraju (daju grešku u vremenu prevođenja) ako pokušate pristupiti enumeratoru koji ne postoji.

Osim zamrzavanja strukture lažnog enuma kako bi se spriječilo slučajno / zlonamjerno dodavanje dodatnih vrijednosti, niti jedan od ostalih odgovora ne utječe na ovu internu značajku Enumusa.

Kao što vjerojatno znate, pristup nepostojećim elementima u JavaScriptu jednostavno vraća undefined i ne eksplodira vaš kod. Budući da su popisivači unaprijed definirane konstante (tj. Dani u tjednu), ne bi smjelo biti slučaja u kojem bi popisivač trebao biti nedefiniran.

Nemojte me pogrešno razumjeti, JavaScript ponašanje povratka undefined pri pristupanju nedefiniranim svojstvima zapravo je vrlo moćna funkcija jezika, ali ne i funkcija koja vam je potrebna kada pokušavate modelirati tradicionalne strukture Enuma.

Ovdje proxy objekti sjaje. Zamjenici su standardizirani u jeziku uz uvođenje ES6 (ES2015). Ovdje je opis iz MDN-a:

Proxy objekt se koristi za definiranje prilagođenog ponašanja za osnovne operacije (na primjer, traženje svojstava, dodjela, brojenje, poziv funkcije, itd.).

Poput proxy poslužitelja web-poslužitelja, JavaScript proxy poslužitelji mogu presresti operacije na objektima (koristeći zamke, nazvati ih kukama ako želite) i omogućiti vam provođenje različitih provjera, radnji i / ili manipulacija prije njihovog završetka (ili u nekim slučajevima općenito, prestanak rada, što želimo učiniti ako i kada pokušamo uputiti na popisivača koji ne postoji).

Ovdje je napravljen primjer koji koristi proxy objekt za simulaciju Enums. U ovom primjeru popisivači su standardne HTTP metode (na primjer, "GET", "POST", itd.):

16 марта '18 в 0:09 2018-03-16 00:09 odgovorio je na Govind Rai 16. ožujka u 0:09. 2018-03-16 00:09

Stvaranje književnosti objekta:

 const Modes = { DRAGGING: 'drag', SCALING: 'scale', CLICKED: 'click' }; 
12
26 июля '15 в 15:03 2015-07-26 15:03 odgovor je dat hvdd 26. srpnja '15 u 15:03 2015-07-26 15:03

Ako koristite Backbone , možete dobiti potpuno označenu funkciju nabrajanja (koju pronađe id, ime, prilagođeni članovi) besplatno pomoću 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() 
11
24 апр. odgovor daje Yaroslav 24 travnja. 2012-04-24 17:04 '12 u 5:04 pm 2012-04-24 17:04

Vaši odgovori su previše komplicirani.

 var buildSet = function(array) { var set = {}; for (var i in array) { var item = array[i]; set[item] = item; } return set; } var myEnum = buildSet(['RED','GREEN','BLUE']); // myEnum.RED == 'RED' ...etc 
8
15 мая '14 в 7:20 2014-05-15 07:20 Odgovor je dao Heltor 15. svibnja 2009. u 7:20 2014-05-15 07:20

Promijenio sam Andreovo "Fi" rješenje:

  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 
7
11 апр. Odgovor koji je dao David Miró 11. travnja 2013-04-11 15:11 '13 u 15:11 2013-04-11 15:11

IE8 ne podržava freeze () metodu.
Izvor: http://kangax.github.io/compat-table/es5/ , kliknite "Prikaži zastarjele preglednike"? gore i provjerite IE8 i popravite sjecište redaka stupaca.

U mom trenutnom projektu igre, koji sam koristio u nastavku, jer neki klijenti još uvijek koriste IE8:

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

Također možemo učiniti:

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

ili čak ovo:

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

Ovo potonje čini se najučinkovitijim za niz, smanjujući ukupnu propusnost, ako imate poslužitelj i klijent koji dijele te podatke.
Naravno, sada je vaša dužnost osigurati da ne postoje sukobi u podacima (RE, EX, itd. Moraju biti jedinstveni, kao i 1, 2, itd.). Imajte na umu da ih morate zauvijek održavati radi kompatibilnosti unatrag.

svrha:

 var wildType = CONST_WILD_TYPES.REGULAR; 

Usporedba:

 if (wildType === CONST_WILD_TYPES.REGULAR) { // do something here } 
6
02 июля '15 в 16:03 2015-07-02 16:03 Odgovor daje Manohar Reddy Poreddy Srpanj 02 '15 u 16:03 2015-07-02 16:03

Я придумал этот подход, который моделируется после перечислений на Java. Они безопасны по типу, поэтому вы также можете выполнять проверки instanceof .