Apa sintaks yang disukai untuk mendefinisikan enum dalam JavaScript?

2082

Apa sintaks yang disukai untuk mendefinisikan enum dalam JavaScript? Sesuatu seperti:

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

// later on

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

Atau adakah idiom yang lebih disukai?

David Citron
sumber
132
Jangan gunakan 0sebagai nomor enumerasi. Kecuali itu digunakan untuk sesuatu yang belum diatur. JS memperlakukan false || undefined || null || 0 || "" || '' || NaNsemua sebagai nilai yang sama bila dibandingkan dengan menggunakan ==.
matsko
153
@matsko bukankah itu hanya argumen yang menentang penggunaan ==?
sdm350
6
0 == nullmengembalikan false
mcont
11
Tapi false == 0dan +null == 0(dan konversi ke nomor terjadi kadang-kadang ketika Anda tidak mengharapkan itu), sementara null == undefinedjuga, dan +undefinedadalah NaN(meskipun NaN != NaN).
sanderd17
46
Matriks kesetaraan ganda lebih membingungkan daripada pemformatan otomatis kata microsoft
aaaaaa

Jawaban:

896

Karena 1.8.5 dimungkinkan untuk menyegel dan membekukan objek, jadi tentukan yang di atas sebagai:

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

atau

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

dan voila! JS enum.

Namun, ini tidak mencegah Anda menetapkan nilai yang tidak diinginkan ke variabel, yang sering kali merupakan tujuan utama enum:

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

Salah satu cara untuk memastikan tingkat keamanan jenis yang lebih kuat (dengan enum atau sebaliknya) adalah dengan menggunakan alat seperti TypeScript atau Flow .

Sumber

Harga tidak diperlukan tetapi saya menyimpannya untuk konsistensi.

Artur Czajka
sumber
6
Menurut Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ) itu berlaku untuk Firefox 4, IE 9, Opera 11.60 dan saya tahu itu berfungsi di Chrome.
Artur Czajka
77
Ini adalah jawaban yang tepat sekarang pada tahun 2012. Lebih sederhana: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Anda tidak perlu menentukan id, Anda bisa menggunakan objek kosong untuk membandingkan enum. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas
34
Untuk kompatibilitas mundur,if (Object.freeze) { Object.freeze(DaysEnum); }
bernilai
17
Saya ingin menunjukkan bahwa melakukan ({ monday: {}, dll berarti bahwa jika Anda mengkonversi objek itu ke JSON melalui stringify Anda akan mendapatkan [{"day": {}}]yang tidak akan bekerja.
jcollum
10
@Supuhstar Pendapat saya tentang pertanyaan ini sekarang berbeda. Jangan gunakan pembekuan (), itu sama sekali tidak berguna dan buang-buang waktu untuk melakukan hal-hal "bodoh". Jika Anda ingin mengekspos enum, hanya mengekspos ini: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Membandingkan objek seperti dalam komentar saya sebelumnya adalah JAUH LEBIH BANYAK LEMAH daripada membandingkan angka.
Gabriel Llamas
608

Ini bukan jawaban yang bagus, tetapi saya akan mengatakan itu bekerja dengan baik, secara pribadi

Karena itu, karena tidak masalah apa nilainya (Anda telah menggunakan 0, 1, 2), saya akan menggunakan string yang bermakna jika Anda pernah ingin menampilkan nilai saat ini.

Gareth
sumber
377
Ini dinyatakan dalam jawaban lain, tetapi karena jawaban ini adalah jawaban yang diterima, saya akan memposting ini di sini. Solusi OP benar. Akan lebih baik, jika digunakan dengan Object.freeze(). Ini akan mencegah kode lain dari mengubah nilai enum. Contoh:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth
5
@TolgaE terima kasih atas perpustakaan itu! Itu mengilhami saya untuk tidak hanya mendidihkannya sampai minimum, tetapi juga menambahkan beberapa fitur! Saya telah memalsukan milik Anda dan meletakkan semuanya di sini: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar
3
@Supuhstar Bagus sekali! Saya senang Anda bisa menggunakannya .. Jangan ragu untuk membuat permintaan tarik jika Anda ingin digabung di perpustakaan ini, maka saya dapat memperbarui perpustakaan npm
Tolga E
2
Jika ada yang tertarik, saya telah mengimplementasikan enum tipe-aman mirip dengan bagaimana mereka berada di Jawa. Ini berarti Anda dapat melakukan instanceofpemeriksaan. Misalnya ColorEnum.RED instanceof ColorEnum(pengembalian true). Anda juga dapat menyelesaikan instance dari nama ColorEnum.fromName("RED") === ColorEnum.RED(pengembalian true). Setiap instance juga memiliki a .name()dan .ordinal()metode, dan enum itu sendiri memiliki values()metode yang mengembalikan array semua konstanta.
Vivin Paliath
3
Saya tidak yakin saya setuju dengan saran "string yang bermakna". Enum seharusnya tidak dianggap sebagai string atau angka; mereka adalah tipe data abstrak. Seharusnya tidak mungkin untuk "menampilkan nilai saat ini" tanpa beberapa metode pembantu. Di Jawa dan .NET, ToString()metode ini. Kami JS devs sudah terlalu bergantung pada hal-hal "hanya berfungsi"! Juga, seseorang harus dapat dengan cepat switchpada enum. Membandingkan string lebih lambat daripada membandingkan angka, jadi Anda akan mendapatkan switchkinerja yang sedikit lebih buruk jika Anda menggunakan string daripada bilangan bulat.
Rabadash8820
501

MEMPERBARUI

Terima kasih untuk semua upvotes semua orang, tetapi saya tidak berpikir jawaban saya di bawah ini adalah cara terbaik untuk menulis enum di JavaScript lagi. Lihat posting blog saya untuk lebih jelasnya: Enums in JavaScript .


Mengingatkan nama sudah mungkin:

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

Atau, Anda bisa membuat objek nilai, sehingga Anda dapat memiliki kue dan memakannya juga:

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

Dalam JavaScript, karena ini adalah bahasa yang dinamis, bahkan dimungkinkan untuk menambahkan nilai enum ke set nanti:

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

Ingat, bidang enum (nilai, nama dan kode dalam contoh ini) tidak diperlukan untuk pemeriksaan identitas dan hanya ada untuk kenyamanan. Juga nama properti ukuran itu sendiri tidak perlu kode keras, tetapi juga dapat diatur secara dinamis. Jadi seandainya Anda hanya tahu nama untuk nilai enum baru Anda, Anda masih dapat menambahkannya tanpa masalah:

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

Tentu saja ini berarti bahwa beberapa asumsi tidak lagi dapat dibuat (nilai tersebut mewakili urutan yang benar untuk ukuran misalnya).

Ingat, dalam JavaScript suatu objek sama seperti peta atau tabel hash . Satu set pasangan nama-nilai. Anda dapat mengulanginya atau memanipulasi mereka tanpa tahu banyak tentang mereka sebelumnya.

Contoh

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

Omong-omong, jika Anda tertarik dengan namespace, Anda mungkin ingin melihat solusi saya untuk namespace sederhana dan kuat dan manajemen dependensi untuk JavaScript: Paket JS

Stijn de Witt
sumber
jadi bagaimana Anda akan pergi dan membuat UKURAN hanya jika Anda hanya memiliki namanya?
Johanisma
2
@ Johanisma: Kasus penggunaan itu tidak benar-benar masuk akal untuk enum karena seluruh ide dari mereka adalah bahwa Anda tahu semua nilai sebelumnya. Namun tidak ada yang menghentikan Anda dari menambahkan nilai tambahan nanti dalam Javascript. Saya akan menambahkan contoh untuk jawaban saya.
Stijn de Witt
2
+1 untuk tautan ke pos Anda dengan pendekatan properti. Elegan karena deklarasi dasarnya sederhana, seperti pada OP, dengan fitur properti tambahan saat diinginkan.
selamat tinggal
@Stijin, sangat menyukai solusi Anda yang diperbarui. Diposting kode dalam komentar di blog Anda dan sebagai komentar di bawah ini. Pada dasarnya, menggunakan fungsi, lakukan properti build dari daftar hash yang ada dan membekukannya secara opsional (mkenum_2 dalam daftar saya). Bersulang.
Andrew Philips
Ada juga perpustakaan yang mengimplementasikannya, juga termasuk fitur bagus untuk perbandingan dan pencarian terbalik: github.com/adrai/enum
Roman M
83

Intinya: Anda tidak bisa.

Anda bisa memalsukannya, tetapi Anda tidak akan mendapatkan keamanan tipe. Biasanya ini dilakukan dengan membuat kamus sederhana dari nilai string yang dipetakan ke nilai integer. Sebagai contoh:

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

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

Masalah dengan pendekatan ini? Anda dapat secara tidak sengaja mendefinisikan ulang enumerant Anda, atau secara tidak sengaja memiliki nilai enumeran duplikat. Sebagai contoh:

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

Edit

Bagaimana dengan Object.freeze Artur Czajka? Bukankah itu berhasil untuk mencegah Anda menetapkan hari Senin hingga Kamis? - Fry Quad

Tentu saja, Object.freezeakan benar-benar memperbaiki masalah yang saya keluhkan. Saya ingin mengingatkan semua orang bahwa ketika saya menulis di atas,Object.freeze tidak benar-benar ada.

Sekarang .... sekarang membuka beberapa sangat kemungkinan yang menarik.

Edit 2
Inilah perpustakaan yang sangat bagus untuk membuat enum.

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

Meskipun mungkin tidak cocok dengan setiap penggunaan enum yang valid, ia berjalan sangat jauh.

Randolpho
sumber
103
ada tipe keamanan di javascript?
Scott Evernden
3
Jadi jangan memetakan nilai ke properti objek. Gunakan getter untuk mengakses enumerant (disimpan sebagai properti, katakanlah, objek "pribadi"). Implementasi naif akan terlihat seperti -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax
2
@Scott Evernden: poin diambil. @ Kangax: intinya adalah masih hack. Enum tidak ada di Javascript, titik, akhir cerita. Bahkan pola yang disarankan oleh Tim Sylvester masih merupakan hack yang kurang ideal.
Randolpho
2
Memercikkan kode dengan literal sangat tidak bisa dipertahankan sehingga masuk akal untuk membuat konstanta untuk itu. Tentu saja Javascript juga tidak memiliki konstanta. Jadi pada dasarnya ini hanyalah cara untuk menulis kode bersih. Itu tidak bisa ditegakkan, tetapi tidak banyak yang bisa di Javascript. Anda dapat mendefinisikan ulang konstanta, atau fungsi, atau sebagian besar apa pun. EG: document.getElementById = function () {alert ("Anda kacau. Javascript bukan typesafe.");};
Stijn de Witt
3
@ Randolpho: Bagaimana dengan Object.freeze Artur Czajka? Bukankah itu berhasil untuk mencegah Anda menetapkan hari Senin hingga Kamis?
Michael - Di mana Clay Shirky
56

Inilah yang kita semua inginkan:

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

Sekarang Anda dapat membuat enum:

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

Dengan melakukan ini, konstanta dapat diakses dengan cara biasa (YesNo.YES, Color.GREEN) dan mereka mendapatkan nilai int sekuensial (TIDAK = 0, YA = 1; MERAH = 0, HIJAU = 1, BIRU = 2).

Anda juga dapat menambahkan metode, dengan menggunakan 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 */
};


Sunting - peningkatan kecil - sekarang dengan varargs: (sayangnya itu tidak berfungsi dengan baik pada IE: S ... harus tetap dengan versi sebelumnya)

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

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');
Andre 'Fi'
sumber
Suka kesederhanaan jawaban ini!
Marquizzo
@ Marsquizzo (dan OP) Saya membuat versi yang lebih baik berdasarkan jawaban ini: stackoverflow.com/a/60309416/1599699
Andrew
53

Di sebagian besar browser modern, ada simbol tipe data primitif yang dapat digunakan untuk membuat enumerasi. Ini akan memastikan keamanan jenis enum karena setiap nilai simbol dijamin oleh JavaScript menjadi unik, yaitu Symbol() != Symbol(). Sebagai contoh:

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

Untuk menyederhanakan debugging, Anda bisa menambahkan deskripsi ke nilai enum:

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

Demo plunker

Pada GitHub Anda dapat menemukan pembungkus yang menyederhanakan kode yang diperlukan untuk menginisialisasi 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
Vitalii Fedorenko
sumber
Ini adalah jawaban yang benar dalam teori. Dalam praktiknya, dukungan browser 2015 masih jauh dari memadai. Sejauh ini belum siap berproduksi.
vbraun
1
Meskipun dukungan browser belum ada, ini adalah jawaban terbaik karena ini dekat dengan apa Symbolyang dimaksudkan.
rvighne
2
Meskipun ... nilai-nilai sering harus serializable, dan Simbol tidak begitu berguna untuk membuat serial dan deserialize.
Andy
3
Apakah hanya saya atau Object.freezehanya untuk orang-orang yang belum menerima kenyataan bahwa "monkeypatch dengan risiko Anda sendiri" adalah kontrak sosial JS?
Andy
@Andy ya serialisasi menyebalkan. Saya akhirnya melakukan eksplisit toJSONpada kelas yang mengandung untuk menggunakan pendekatan ini: stackoverflow.com/questions/58499828/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功 法轮功
30

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

Mari kita langsung ke masalah: ukuran file. Setiap jawaban lain yang tercantum di sini membesar-besarkan kode Anda. Saya sampaikan kepada Anda bahwa untuk kinerja terbaik, keterbacaan kode, manajemen proyek skala besar, petunjuk sintaksis dalam banyak editor kode, dan pengurangan ukuran kode dengan minifikasi, ini adalah cara yang tepat untuk melakukan enumerasi: variabel garis bawah-notasi.


wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwv

Variabel Garis Bawah-Notasi

Seperti ditunjukkan dalam bagan di atas dan contoh di bawah, berikut adalah lima langkah mudah untuk memulai:

  1. Tentukan nama untuk grup enumerasi. Pikirkan kata benda yang dapat menggambarkan tujuan pencacahan atau setidaknya entri dalam pencacahan. Misalnya, sekelompok enumerasi yang mewakili warna yang dipilih oleh pengguna mungkin lebih baik bernama COLORCHOICES daripada COLORS.
  2. Putuskan apakah pencacahan dalam grup adalah saling eksklusif atau independen. Jika saling eksklusif, mulai setiap nama variabel yang disebutkan dengan ENUM_. Jika independen atau berdampingan, gunakanINDEX_ .
  3. Untuk setiap entri, buat variabel lokal baru yang namanya dimulai dengan ENUM_atauINDEX_ , lalu nama grup, lalu garis bawah, lalu nama ramah unik untuk properti
  4. Tambahkan ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, atau INDEXLEN_(apakah LEN_atauLENGTH_ preferensi pribadi) variabel disebutkan di akhir. Anda harus menggunakan variabel ini sedapat mungkin dalam kode Anda untuk memastikan bahwa menambahkan entri tambahan ke enumerasi dan menambah nilai ini tidak akan merusak kode Anda.
  5. Berikan masing-masing variabel enumerated berturut-turut nilai satu lebih dari yang terakhir, mulai dari 0. Ada komentar di halaman ini yang mengatakan 0tidak boleh digunakan sebagai nilai disebutkan karena 0 == null, 0 == false, 0 == "", dan kegilaan lainnya JS. Saya serahkan kepada Anda bahwa, untuk menghindari masalah ini dan meningkatkan kinerja pada saat yang sama, selalu gunakan ===dan jangan pernah biarkan ==muncul dalam kode Anda kecuali dengan typeof(ex typeof X == "string"). Dalam semua tahun saya menggunakan ===, saya tidak pernah memiliki masalah dengan menggunakan 0 sebagai nilai enumerasi. Jika Anda masih mudah tersinggung, maka 1dapat digunakan sebagai nilai awal dalam ENUM_enumerasi (tetapi tidak dalam INDEX_enumerasi) tanpa penalti kinerja dalam banyak kasus.
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
}

Berikut adalah bagaimana saya ingat kapan harus digunakan INDEX_dan kapan harus digunakan ENUM_:

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

Namun, ENUM_dapat, dalam keadaan tertentu, sesuai sebagai indeks seperti ketika menghitung kejadian setiap item.

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

Perhatikan bahwa, dalam kode di atas, sangat mudah untuk menambahkan jenis hewan peliharaan baru: Anda hanya perlu menambahkan entri baru setelah ENUM_PET_RATdan memperbarui ENUMLEN_PETsesuai. Mungkin lebih sulit dan sulit untuk menambahkan entri baru dalam sistem enumerasi lain.


wvwwvw wvwvwvw wvwxwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

Selain itu, sintaks enumerasi ini memungkinkan perluasan kelas yang jelas dan ringkas seperti yang terlihat di bawah ini. Untuk memperluas kelas, tambahkan nomor yang bertambah ke LEN_entri kelas induk. Kemudian, selesaikan subclass dengan LEN_entri sendiri sehingga subclass dapat diperpanjang lebih lanjut di masa depan.

Diagram ekstensi tambahan

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

(Panjang: 2,450 byte)

Beberapa orang mungkin mengatakan bahwa ini kurang praktis daripada solusi lain: ia membutuhkan banyak ruang, butuh waktu lama untuk menulis, dan tidak dilapisi dengan sintaksis gula. Orang-orang itu akan benar jika mereka tidak mengubah kode mereka. Namun, tidak ada orang yang berakal yang akan meninggalkan kode yang tidak ditentukan dalam produk akhir. Untuk minifikasi ini, Closure Compiler adalah yang terbaik yang belum saya temukan. Akses online dapat ditemukan di sini . Compiler Closure dapat mengambil semua data enumerasi ini dan memasangnya, membuat Javascript Anda menjadi super duper kecil dan menjalankan super duper dengan cepat. Jadi, Minify with Closure Compiler. Mengamati.


wvwwvw wvwvwvw wvwxwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

Compiler Closure dapat melakukan beberapa optimasi yang sangat luar biasa melalui inferensi yang jauh melampaui kapasitas dari pengubah Javascript lainnya. Closure Compiler dapat inline variabel primitif diatur ke nilai tetap. Closure Compiler juga dapat membuat kesimpulan berdasarkan pada nilai yang diuraikan ini dan menghilangkan blok yang tidak digunakan dalam if-statement dan loop.

Kode peras melalui Closure Compiler

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

(Panjang: 605 byte)

Closure Compiler memberi Anda penghargaan untuk pengkodean yang lebih cerdas dan mengorganisasikan kode Anda dengan baik karena, sementara banyak minifiers menghukum kode terorganisir dengan ukuran file yang lebih kecil, Closure Compiler dapat menyaring semua kebersihan dan kewarasan Anda untuk menghasilkan ukuran file yang lebih kecil jika Anda menggunakan trik seperti enumerasi nama variabel. Itu, dalam satu pikiran ini, adalah grail suci pengkodean: alat yang keduanya membantu kode Anda dengan ukuran yang lebih kecil dan membantu pikiran Anda dengan melatih kebiasaan pemrograman yang lebih baik.


wvwwvw wvwvwvw wvwxwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

Sekarang, mari kita lihat seberapa besar file ekuivalen tanpa enumerasi ini.

Sumber Tanpa Menggunakan Pencacahan (panjang: 1,973 byte (477 byte lebih pendek dari kode yang disebutkan!))
Diperkecil Tanpa Menggunakan Pencacahan (panjang: 843 byte ( lebih panjang 238 byte dari kode pencacahan ))

Bagan ukuran kode



Seperti terlihat, tanpa enumerasi, kode sumber lebih pendek dengan biaya kode minified yang lebih besar. Saya tidak tahu tentang Anda; tapi saya tahu pasti bahwa saya tidak memasukkan kode sumber ke dalam produk akhir. Jadi, bentuk enumerasi ini jauh lebih unggul sehingga menghasilkan ukuran file yang lebih kecil.


wvwwvw wvwvwvw wvwxwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

Keuntungan lain tentang bentuk enumerasi ini adalah dapat digunakan untuk mengelola proyek skala besar dengan mudah tanpa mengorbankan ukuran kode yang diperkecil. Ketika mengerjakan proyek besar dengan banyak orang lain, mungkin bermanfaat untuk secara eksplisit menandai dan memberi label nama variabel dengan siapa yang membuat kode sehingga pembuat kode asli dapat dengan cepat diidentifikasi untuk memperbaiki bug kolaboratif.

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

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

Selanjutnya, bentuk enumerasi ini juga jauh lebih cepat setelah minifikasi. Dalam properti bernama normal, browser harus menggunakan hashmaps untuk mencari di mana properti itu pada objek. Meskipun kompiler JIT secara cerdas men-cache lokasi ini pada objek, masih ada overhead yang luar biasa karena kasus-kasus khusus seperti menghapus properti yang lebih rendah dari objek.

Tetapi, dengan array PACKED_ELEMENTS integer non-sparse yang diindeks terus menerus , browser dapat melewati sebagian besar overhead itu karena indeks nilai dalam array internal sudah ditentukan. Ya, menurut standar ECMAScript, semua properti seharusnya diperlakukan sebagai string. Namun demikian, aspek ini dari standar ECMAScript sangat menyesatkan tentang kinerja karena semua browser memiliki optimasi khusus untuk indeks numerik dalam array.

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

Bandingkan kode di atas dengan kode di bawah ini.

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

Orang mungkin keberatan dengan kode dengan enumerasi yang nampaknya jauh lebih lama daripada kode dengan objek biasa, tetapi tampilannya bisa menipu. Penting untuk diingat bahwa ukuran kode sumber tidak sebanding dengan ukuran keluaran saat menggunakan Kompilator Penutupan epik. Mengamati.

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

Kode yang diperkecil tanpa enumerasi di atas dan kode yang diperkecil dengan enumerasi di bawah.

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

Contoh di atas menunjukkan bahwa, selain memiliki kinerja yang unggul, kode yang disebutkan juga menghasilkan ukuran file yang diperkecil lebih kecil.


wvwwvw wvwvwvw wvwxwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

Selain itu, ceri pribadi yang satu ini di atas menggunakan bentuk enumerasi ini bersama dengan pengecualian CodeMirror editor teks dalam mode Javascript. Mode penyorotan Javascript CodeMirror menyoroti variabel lokal dalam cakupan saat ini. Dengan begitu, Anda langsung tahu kapan Anda mengetikkan nama variabel dengan benar karena jika nama variabel sebelumnya dinyatakan dengan ketika mengeksekusi kode dengan nama enumerasi yang salah ketik. Juga, alat JavaScript seperti JSLint dan Closure Compiler sangat keras untuk memberi tahu Anda ketika Anda salah ketik dalam nama variabel enumerasi. CodeMirror, browser, dan berbagai alat Javascript disatukan membuat debugging enumerasi bentuk ini sangat sederhana dan sangat mudah.var kata kunci, maka nama variabel tersebut akan mengubah warna khusus (cyan secara default). Bahkan jika Anda tidak menggunakan CodeMirror, maka setidaknya browser memberikan bantuan[variable name] is not defined

CodeMirror menyoroti demonstrasi

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
}

Dalam cuplikan di atas, Anda diberitahu dengan kesalahan karena ENUM_COLORENUM_DNEtidak ada.


wvwwvw wvwvwvw wvwxwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Saya pikir aman untuk mengatakan bahwa metodologi enumerasi ini memang cara terbaik untuk pergi tidak hanya untuk ukuran kode yang diperkecil, tetapi juga untuk kinerja, debugging, dan kolaborasi.

Setelah membaca pertanyaan yang bermanfaat, saya berterima kasih kepada penulis karena telah meluangkan waktu dalam tulisan mereka dengan mengklik tanda panah kiri atas di kotak pertanyaan. Setiap kotak jawaban juga memiliki salah satu dari panah atas ini.

Jack Giffin
sumber
Eh. Saya sangat suka keterbacaan dan kemudahan penggunaan dan pemahaman untuk ukuran kode.
Andrew
1
@Andrew Dengan jawaban saya, Anda dapat memiliki keduanya. Jawaban saya menghasilkan cara termudah untuk menggunakan / mengelola kode dan dalam ukuran kode terkecil yang diperkecil.🙂
Jack Giffin
1
@Andrew Saya telah mencoba menerapkan Yet Another Enum Anda (YEA!) Pada contoh pengurai warna dalam jawaban saya. Namun, saya telah menemukan beberapa masalah yang mungkin ingin Anda atasi. YEA tidak memiliki cara untuk memperpanjang enumerasi dengan subclass, memaksa saya untuk membuat kelas induk dan anak yang terpisah, yang bisa sangat sulit untuk dikelola pada proyek-proyek besar. YEA tidak memastikan entri ada (ex colors.REEDyields undefined), jadi kesalahan ketik membuat teka-teki yang sulit dipahami. YEA tidak membedakan antara penggunaan enumerasi sebagai indeks dan ID, yang menyebabkan kode membingungkan di mana semuanya terlihat sama. ...
Jack Giffin
1
@Andrew ... YEA menghalangi kemampuan Closure Compiler untuk memperkecil. Bandingkan kode sumber dengan YEA (3549 byte) dengan kode yang diperkecil dengan YEA (1344 byte) dengan kode yang diperkecil dengan solusi saya (604 byte). Akhirnya, YEA melibatkan "pemetaan berdasarkan nama" karena memisahkan nama string dari ID yang disebutkan. Milik saya hanya mempertimbangkan ID, jadi tidak ada "pemetaan berdasarkan nama" yang diperlukan, yang mengarah ke desain yang lebih sederhana dan kinerja yang lebih baik. Terima kasih telah membagikan solusi Anda, tetapi perlu banyak perbaikan sebelum dapat praktis.
Jack Giffin
1
@Andrew Anda berhak atas pendapat Anda karena saya harus menambang 👍
Jack Giffin
23

Saya telah bermain-main dengan ini, karena saya suka enum saya. =)

Menggunakan Object.definePropertySaya pikir saya datang dengan solusi yang agak layak.

Inilah jsfiddle: http://jsfiddle.net/ZV4A6/

Dengan menggunakan metode ini .. Anda harus (secara teori) dapat memanggil dan menentukan nilai enum untuk objek apa pun, tanpa memengaruhi atribut lain dari objek itu.

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

Karena atribut writable:falseini seharusnya membuatnya aman.

Jadi, Anda harus dapat membuat objek khusus, lalu memanggilnya Enum(). Nilai yang ditetapkan mulai dari 0 dan naik per item.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3
Duncan
sumber
3
Jika Anda menambahkan return this;pada akhir Enum yang dapat Anda lakukan:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP
Saya tidak mempertimbangkan itu, karena itu bukan metode normal saya dalam melakukan sesuatu. Tapi kamu benar sekali! Saya akan mengeditnya.
Duncan
Saya sangat suka ini meskipun saya bukan penggemar berat objek ruang (dengan fungsi global ENUM). Konversikan ini ke fungsi mkenum dan tambahkan tugas numerik opsional => var mixedUp = mkenum ('BLACK', {RED: 0x0F00, BLUE: 0X0F, GREEN: 0x0F0, WHITE: 0x0FFF, ONE: 1}, DUA, TIGA, EMPAT) ; // Menambahkan kode saya sebagai jawaban di bawah ini. Terima kasih.
Andrew Philips
Sejujurnya, saya bahkan tidak menggunakan ini lagi. Saya telah menggunakan Google's Closure Compiler, dan ini tidak berfungsi dengan baik (atau hanya mempersulit) jika Anda menggunakan pengaturan Advanced. Jadi saya baru saja kembali ke notasi objek standar.
Duncan
1
falseadalah standar untuk writable, enumerabledan configurable. Tidak perlu mengunyah default.
ceving
23

Gunakan Javascript Proxies

TLDR: Tambahkan kelas ini ke metode utilitas Anda dan gunakan di seluruh kode Anda, itu mencemooh perilaku Enum dari bahasa pemrograman tradisional, dan benar-benar melempar kesalahan ketika Anda mencoba mengakses enumerator yang tidak ada atau menambah / memperbarui enumerator. Tidak perlu bergantung 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);
  }
}

Kemudian buat enum dengan membuat instance kelas:

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

Penjelasan Lengkap:

Salah satu fitur yang sangat bermanfaat dari Enums yang Anda dapatkan dari bahasa tradisional adalah bahwa mereka meledak (melemparkan kesalahan waktu kompilasi) jika Anda mencoba mengakses enumerator yang tidak ada.

Selain membekukan struktur enum yang diolok-olok untuk mencegah nilai-nilai tambahan tidak sengaja / jahat ditambahkan, tidak ada jawaban lain yang membahas fitur intrinsik dari Enum.

Seperti yang mungkin Anda ketahui, mengakses anggota yang tidak ada dalam JavaScript hanya mengembalikan undefineddan tidak meledakkan kode Anda. Karena pencacah adalah konstanta yang ditentukan sebelumnya (yaitu hari dalam seminggu), seharusnya tidak pernah ada kasus ketika pencacah harus tidak ditentukan.

Jangan salah, perilaku JavaScript untuk kembali undefinedketika mengakses properti yang tidak ditentukan sebenarnya adalah fitur bahasa yang sangat kuat, tetapi itu bukan fitur yang Anda inginkan ketika Anda mencoba untuk mengejek struktur Enum tradisional.

Di sinilah objek Proxy bersinar. Proxy distandarisasi dalam bahasa dengan diperkenalkannya ES6 (ES2015). Berikut deskripsi dari MDN:

Objek Proxy digunakan untuk menentukan perilaku kustom untuk operasi mendasar (mis. Pencarian properti, penugasan, enumerasi, pemanggilan fungsi, dll).

Mirip dengan proxy server web, proxy JavaScript dapat mencegat operasi pada objek (dengan menggunakan "jebakan", menyebutnya kait jika Anda mau) dan memungkinkan Anda untuk melakukan berbagai pemeriksaan, tindakan, dan / atau manipulasi sebelum selesai (atau dalam beberapa kasus menghentikan operasi sama sekali yang persis apa yang ingin kita lakukan jika dan ketika kita mencoba merujuk seorang enumerator yang tidak ada).

Berikut adalah contoh yang dibuat yang menggunakan objek Proxy untuk meniru Enums. Enumerator dalam contoh ini adalah Metode HTTP standar (yaitu "DAPATKAN", "POST", dll.):

// 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: Apa itu proxy?

Saya ingat ketika saya pertama kali melihat kata proxy di mana-mana, itu jelas tidak masuk akal bagi saya untuk waktu yang lama. Jika itu Anda saat ini, saya pikir cara mudah untuk menggeneralisasi proxy adalah dengan menganggapnya sebagai perangkat lunak, institusi, atau bahkan orang yang bertindak sebagai perantara atau perantara antara dua server, perusahaan, atau orang.

Govind Rai
sumber
Bagaimana melakukan sesuatu seperti myEnum.valueOf ("someStringValue")? Diharapkan: jika string input memiliki nilai elemen enumerator, harus mengembalikan item. Jika tidak ada item yang memiliki nilai string itu, lempar pengecualian.
sscarduzio
@ sscarduzio Anda dapat mengganti valueOfmetode default dengan menetapkannya sebagai metode instance pada kelas Enum. Namun, mengapa Anda ingin mengaksesnya dengan cara ini dibandingkan hanya mengaksesnya melalui notasi titik?
Govind Rai
Enum saya adalah const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}) dan saya parse dari input string "info" atau "debug" string sembarang. Jadi saya memerlukan sesuatu seperti currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio
1
Kenapa kau tidak bisa melakukannya logLevelEnum[settings.get("log_level")]? menambahkan parseOrThrowhanya akan berulang pada apa yang sudah dilakukan perangkap proxy untuk Anda.
Govind Rai
17

Ini adalah yang lama yang saya tahu, tetapi cara itu telah dilaksanakan melalui antarmuka TypeScript adalah:

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

Ini memungkinkan Anda untuk mencari MyEnum.Baryang mengembalikan 1, dan MyEnum[1]yang mengembalikan "Bilah" terlepas dari urutan deklarasi.

Rob Hardy
sumber
1
Plus MyEnum ["Bar"] berfungsi dengan mengembalikan 1 ... <3 Jenis Script sejauh ini ...
David Karlaš
3
dan tentu saja jika Anda benar-benar menggunakan naskah:enum MyEnum { Foo, Bar, Foobar }
parlemen
16

Di ES7 , Anda dapat melakukan ENUM yang elegan dengan mengandalkan atribut statis:

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

kemudian

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

Keuntungan (menggunakan kelas daripada objek literal) adalah memiliki kelas induk Enummaka semua Enums Anda akan memperluas kelas itu.

 class ColorEnum  extends Enum {/*....*/}
Abdennour TOUMI
sumber
4
Bisakah Anda menjelaskan mengapa memiliki kelas induk adalah keuntungan? Saya merasa seperti kehilangan sesuatu!
Jon G
7
Jangan lakukan itu. new ColorEnum()sama sekali tidak masuk akal.
Bergi
3
memperpanjang enum terdengar gila, sungguh
Codii
sekali bahasa tidak mendukungnya secara alami akan masuk akal simpan konvensi ini dan gunakan seperti ini! saya setuju!
xpto
Saya pikir (?) Apa yang OP maksudkan adalah: Manfaat dari static murni adalah bahwa ia tersedia di mana-mana sebagai singleton, dan Anda tidak perlu membuat instance kelas - OP tidak menyarankan Anda melakukannya! Saya pikir apa yang dia katakan adalah bahwa superclass Enummemiliki standar statis metode pencacah di atasnya, seperti getValues(), getNames(), iterate(), dll Jika itu yang terjadi, Anda tidak perlu reimplement mereka untuk setiap jenis baru enum.
Insinyur
15

Ini adalah solusi yang saya gunakan.

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

Dan Anda mendefinisikan enum Anda seperti ini:

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

Dan ini adalah bagaimana Anda mengakses enum Anda:

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

Saya biasanya menggunakan 2 metode terakhir untuk memetakan enums dari objek pesan.

Beberapa keuntungan dari pendekatan ini:

  • Mudah untuk mendeklarasikan enum
  • Mudah mengakses enum Anda
  • Enum Anda bisa menjadi tipe yang kompleks
  • Kelas Enum memiliki beberapa caching asosiatif jika Anda sering menggunakan getByValue

Beberapa kelemahan:

  • Beberapa manajemen memori berantakan terjadi di sana, karena saya menyimpan referensi ke enum
  • Masih tidak ada tipe keselamatan
Chris
sumber
14

Buat objek literal:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};
hvdd
sumber
12
consttidak membuat properti objek tidak berubah, itu hanya berarti bahwa variabel Modestidak dapat dipindahkan ke sesuatu yang lain. Untuk membuatnya lebih lengkap, gunakan Object.freeze()bersama const.
rvighne
Tolong jangan gunakan Object.freeze. Ini mencegah Closure Compiler dari inlining objek.
Jack Giffin
11

Jika Anda menggunakan Backbone , Anda bisa mendapatkan fungsionalitas enum full-blown (temukan berdasarkan id, nama, anggota kustom) secara gratis menggunakan 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()
Yaroslav
sumber
8

jawaban Anda terlalu rumit

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
Xeltor
sumber
1
@JackGiffin Saya setuju bahwa jawaban Anda lebih berkinerja dan milik saya dapat mengambil lebih banyak memori, meskipun Anda tidak boleh berasumsi semua orang menginginkan enum cara C ++ menerapkannya. Harap hormati jawaban lain dan pengembang yang mungkin lebih suka yang ini daripada jawaban Anda.
Xeltor
7

Saya telah memodifikasi solusi 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);
    }
  }

Uji:

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

Saya datang dengan pendekatan ini yang dimodelkan setelah enum di Jawa. Ini adalah tipe-aman, sehingga Anda dapat melakukaninstanceof pemeriksaan juga.

Anda dapat mendefinisikan enum seperti ini:

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

Dayssekarang mengacu pada Daysenum:

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"

Pelaksanaan:

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
    }

})();
Vivin Paliath
sumber
Kelihatannya bagus, mungkin Anda harus memeriksa keberadaan freezemetode untuk kompatibilitas? Misalnya,if (Object.freeze) { Object.freeze(values); }
FBB
Poin bagus! Akan melakukan!
Vivin Paliath
6

IE8 Tidak mendukung metode freeze ().
Sumber: http://kangax.github.io/compat-table/es5/ , Klik pada "Show browser usang?" di atas, dan periksa IE8 & bekukan persimpangan baris col.

Dalam proyek gim saya saat ini, saya telah menggunakan di bawah ini, karena beberapa pelanggan masih menggunakan IE8:

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

Kita juga bisa melakukan:

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

atau bahkan ini:

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

Yang terakhir, tampaknya paling efisien untuk string, ini mengurangi total bandwidth Anda jika Anda memiliki server & klien yang bertukar data ini.
Tentu saja, sekarang tugas Anda untuk memastikan tidak ada konflik dalam data (RE, EX, dll. Harus unik, juga 1, 2, dll. Harus unik). Perhatikan bahwa Anda perlu mempertahankan ini selamanya untuk kompatibilitas mundur.

Tugas:

var wildType = CONST_WILD_TYPES.REGULAR;

Perbandingan:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}
Manohar Reddy Poreddy
sumber
5
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

Anda tidak perlu memastikan bahwa Anda tidak menetapkan angka duplikat ke nilai enum yang berbeda dengan cara ini. Objek baru akan dipakai dan ditugaskan untuk semua nilai enum.

Shivanshu Goyal
sumber
Jawaban ini diremehkan. Itu salah satu ide favorit saya karena kesederhanaannya. Dalam praktiknya, saya pikir saya akan tetap menggunakan string karena lebih mudah untuk debug untuk saat ini.
Domino
Hmm, pastikan saja kode ini tidak dipanggil dua kali ...
Andrew
4

Berikut adalah beberapa cara berbeda untuk mengimplementasikan enum TypeScript .

Cara termudah adalah dengan hanya beralih pada objek, menambahkan pasangan nilai kunci terbalik ke objek. Satu-satunya kelemahan adalah bahwa Anda harus menetapkan nilai secara manual untuk setiap anggota.

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


Dan ini adalah lodin mixin untuk membuat enum menggunakan string. Meskipun versi ini sedikit lebih terlibat, ia melakukan penomoran secara otomatis untuk Anda. Semua metode lodash yang digunakan dalam contoh ini memiliki standar JavaScript yang setara, sehingga Anda dapat dengan mudah mengubahnya jika Anda mau.

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
Blake Bowen
sumber
sangat pintar, terima kasih
Ilan
4

Saya baru saja menerbitkan paket NPM gen_enum memungkinkan Anda membuat struktur data Enum dalam Javascript dengan cepat:

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 

Satu hal yang menyenangkan tentang alat kecil ini adalah di lingkungan modern (termasuk browser nodejs dan IE 9+) objek Enum yang dikembalikan tidak dapat diubah.

Untuk informasi lebih lanjut silakan checkout https://github.com/greenlaw110/enumjs

Pembaruan

Saya gen_enumpaket usang dan menggabungkan fungsi ke dalam paket constjs , yang menyediakan lebih banyak fitur termasuk objek yang tidak dapat diubah, deserialisasi string JSON, konstanta string dan pembuatan bitmap dll. Checkout https://www.npmjs.com/package/constjs untuk informasi lebih lanjut

Untuk meng-upgrade dari gen_enumke constjshanya mengubah pernyataan

var genEnum = require('gen_enum');

untuk

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

Solusi paling sederhana:

Membuat

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

Dapatkan Nilai

console.log(Status.Ready) // 1

Dapatkan Kunci

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

Saya telah membuat kelas Enum yang dapat mengambil nilai DAN nama di O (1). Itu juga dapat menghasilkan Array Objek yang berisi semua Nama dan Nilai.

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

Anda dapat menggunakannya seperti ini:

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

Untuk mengambil nilai (seperti Enums di C #):

var val2 = enum1.item2;

Untuk mengambil nama untuk suatu nilai (dapat bersifat mendua ketika menempatkan nilai yang sama untuk nama yang berbeda):

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

Untuk mendapatkan array dengan setiap nama & nilai dalam suatu objek:

var arr = enum1.GetObjArr();

Akan menghasilkan:

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

Anda juga bisa mendapatkan opsi pemilihan html dengan mudah:

var html = enum1.GetSelectOptionsHTML();

Yang berlaku:

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

Meskipun hanya metode statis (dan bukan properti statis) yang didukung dalam ES2015 (lihat di sini juga, §15.2.2.2), anehnya Anda dapat menggunakan di bawah ini dengan Babel dengan 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);

Saya menemukan ini berfungsi seperti yang diharapkan bahkan di seluruh modul (misalnya mengimpor CellStateenum dari modul lain) dan juga ketika saya mengimpor modul menggunakan Webpack.

Keuntungan metode ini memiliki lebih dari sebagian besar jawaban lain adalah bahwa Anda dapat menggunakannya bersama pemeriksa tipe statis (misalnya Flow ) dan Anda dapat menegaskan, pada saat pengembangan menggunakan pengecekan tipe statis, bahwa variabel, parameter, dll. Anda adalah spesifikCellState " enum "daripada enum lain (yang tidak mungkin dibedakan jika Anda menggunakan objek atau simbol umum).

memperbarui

Kode di atas memiliki kekurangan karena memungkinkan seseorang untuk membuat objek tipe tambahan CellState(walaupun seseorang tidak dapat menetapkannya ke bidang statis CellStatesejak dibekukan). Namun, kode lebih halus di bawah ini menawarkan keuntungan sebagai berikut:

  1. tidak ada lagi objek bertipe CellStatedapat dibuat
  2. Anda dijamin tidak ada dua instance enum yang diberi kode yang sama
  3. metode utilitas untuk mendapatkan enum kembali dari representasi string
  4. yang valuesfungsi yang kembali semua contoh enum tidak harus menciptakan nilai kembali di atas, pengguna (dan rawan kesalahan) cara.

    '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;
Marcus Junius Brutus
sumber
Contoh yang bagus :-)
Ashraf.Shk786
4

es7 way, (iterator, freeze), penggunaan:

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'

kode:

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

}
Joseph Merdrignac
sumber
4

Ini adalah bagaimana typescript menerjemahkannya enumke dalam Javascript:

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

Sekarang:

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

Pada awalnya saya bingung mengapa obj[1]kembali 'Active', tetapi kemudian menyadari bahwa mati sederhana - operator Tugas memberikan nilai dan kemudian mengembalikannya:

obj['foo'] = 1
// => 1
Julius Dzidzevičius
sumber
4

Anda dapat melakukan sesuatu seperti ini

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

Seperti yang didefinisikan di perpustakaan ini. https://github.com/webmodule/foo/blob/master/foo.js#L217

Contoh lengkap https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026

LNT
sumber
3

Cara cepat dan sederhana adalah:

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

console.log(Colors.WHITE)  //this prints out "0"
pengguna2254487
sumber
6
Fungsi ini tidak perlu dan memberi Anda hasil yang sama persis seperti apa yang diposting OP.
Sildoreth
3

Pada saat penulisan, Oktober 2014 - jadi di sini adalah solusi kontemporer. Saya menulis solusi sebagai Modul Node, dan telah menyertakan tes menggunakan Mocha dan Chai, serta garis bawahJS. Anda dapat dengan mudah mengabaikannya, dan ambil saja kode Enum jika diinginkan.

Melihat banyak posting dengan pustaka yang terlalu berbelit-belit, dll. Solusi untuk mendapatkan dukungan enum dalam Javascript sangat sederhana sehingga sebenarnya tidak diperlukan. Ini kodenya:

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;

Dan tes untuk menggambarkan apa yang memberi Anda:

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

});

Seperti yang Anda lihat, Anda mendapatkan pabrik Enum, Anda bisa mendapatkan semua kunci hanya dengan memanggil enum.keys, dan Anda bisa mencocokkan kunci itu sendiri dengan konstanta integer. Dan Anda dapat menggunakan kembali pabrik dengan nilai yang berbeda, dan mengekspor Enum yang dihasilkan menggunakan pendekatan modular Node.

Sekali lagi, jika Anda hanya pengguna biasa, atau di peramban dll, ambil saja bagian kode pabrik, berpotensi menghapus pustaka garis bawah juga jika Anda tidak ingin menggunakannya dalam kode Anda.

arcseldon
sumber
5
Bisakah Anda mengirim jawaban hanya dengan "inilah cara melakukan ini sebagai pengguna biasa yang hanya ingin enum, bukan pabrik, garis bawah, atau apa pun yang mewah"?
GreenAsJade
5
Meskipun ini cukup luar biasa dari eyepoint pengembang, itu tidak terlalu bersih atau mudah dibaca. Solusi Enum dari OP lebih mudah dan lebih mudah dibaca dalam segala hal, dan karenanya lebih baik digunakan. Meski begitu, cukup menakjubkan bahwa Anda datang dengan ini.
David
3

Mudah digunakan, saya pikir. 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);

MEMPERBARUI:

Ada kode pembantu saya ( TypeHelper).

Sherali Turdiyev
sumber