Cú pháp ưa thích để xác định enum trong JavaScript là gì?


2082

Cú pháp ưa thích để xác định enum trong JavaScript là gì? Cái gì đó như:

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

// later on

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

Hoặc có một thành ngữ thích hợp hơn?


132
Đừng sử dụng 0như một số liệt kê. Trừ khi nó được sử dụng cho một cái gì đó chưa được thiết lập. JS coi false || undefined || null || 0 || "" || '' || NaNtất cả là cùng một giá trị khi so sánh sử dụng ==.
Matsko

153
@matsko không phải là một đối số chống lại việc sử dụng ==?
sdm350

6
0 == nulltrả về false
mcont

11
Nhưng false == 0+null == 0(và chuyển đổi đến các số xảy ra đôi khi bạn không mong đợi nó), trong khi null == undefinedquá, và +undefinedNaN(mặc dù NaN != NaN).
sanderd17

46
Ma trận đẳng thức kép khó hiểu hơn định dạng tự động của microsoft word
aaaaaa

Câu trả lời:


896

Kể từ 1.8.5, có thể niêm phong và đóng băng đối tượng, vì vậy hãy xác định các mục trên là:

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

hoặc là

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

và Voila! Enum JS.

Tuy nhiên, điều này không ngăn bạn gán giá trị không mong muốn cho một biến, thường là mục tiêu chính của enums:

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

Một cách để đảm bảo mức độ an toàn của loại mạnh hơn (với enums hoặc cách khác) là sử dụng một công cụ như TypeScript hoặc Flow .

Nguồn

Báo giá không cần thiết nhưng tôi giữ chúng cho thống nhất.


6
Theo Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ) nó có thể áp dụng cho Firefox 4, IE 9, Opera 11.60 và tôi biết nó hoạt động trong Chrome.
Artur Czajka

77
Đây là câu trả lời đúng trong năm 2012. Đơn giản hơn : var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Bạn không cần chỉ định id, bạn chỉ có thể sử dụng một đối tượng trống để so sánh enums. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas

34
Để tương thích ngược,if (Object.freeze) { Object.freeze(DaysEnum); }
saluce

17
Tôi muốn chỉ ra rằng làm như vậy, ({ monday: {}, v.v. có nghĩa là nếu bạn chuyển đổi đối tượng đó thành JSON thông qua việc xâu chuỗi, bạn sẽ [{"day": {}}]không làm việc.
jcollum

10
@Supuhstar Ý kiến ​​của tôi về câu hỏi này bây giờ đã khác. Đừng sử dụng đóng băng (), nó hoàn toàn vô dụng và lãng phí thời gian để làm những việc "ngu ngốc". Nếu bạn muốn phơi bày một enum, chỉ cần phơi bày điều này : var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. So sánh các đối tượng như trong nhận xét trước đây của tôi RẤT NHIỀU HƠN so với số lượng.
Gabriel Llamas

608

Đây không phải là nhiều câu trả lời, nhưng tôi nói rằng nó hoạt động tốt, cá nhân

Phải nói rằng, vì các giá trị là gì (bạn đã sử dụng 0, 1, 2) không quan trọng, tôi sẽ sử dụng một chuỗi có ý nghĩa trong trường hợp bạn muốn xuất giá trị hiện tại.


377
Điều này đã được nêu trong một câu trả lời khác, nhưng vì câu trả lời này là câu trả lời được chấp nhận, tôi sẽ đăng nó ở đây. Giải pháp của OP là chính xác. Nó sẽ còn tốt hơn, mặc dù, nếu được sử dụng với Object.freeze(). Điều này sẽ ngăn mã khác thay đổi giá trị của enum. Ví dụ:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth

5
@TolgaE cảm ơn bạn vì thư viện đó! Nó truyền cảm hứng cho tôi để không chỉ đun sôi nó đến mức tối thiểu, mà còn thêm một vài tính năng! Tôi đã rẽ nhánh của bạn và đặt tất cả ở đây: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar

3
@Supuhstar Thật tuyệt! Tôi rất vui vì bạn có thể sử dụng nó .. Hãy thoải mái thực hiện yêu cầu kéo nếu bạn muốn nó được hợp nhất trong thư viện này, sau đó tôi có thể cập nhật thư viện npm
Tolga E

2
Nếu bất cứ ai quan tâm, tôi đã triển khai các enum an toàn kiểu tương tự như cách họ đang ở trong Java. Điều này có nghĩa là bạn có thể làm instanceofkiểm tra. Ví dụ ColorEnum.RED instanceof ColorEnum(trả về true). Bạn cũng có thể giải quyết một trường hợp ra khỏi tên ColorEnum.fromName("RED") === ColorEnum.RED(trả về true). Mỗi thể hiện cũng có một .name()và một .ordinal()phương thức và enum có một values()phương thức truy xuất lại một mảng của tất cả các hằng số.
Vivin Paliath

3
Tôi không chắc chắn tôi đồng ý với đề xuất "chuỗi ý nghĩa". Enums không nên được coi là chuỗi hoặc số; chúng là các kiểu dữ liệu trừu tượng. Không thể "xuất giá trị hiện tại" mà không có một số phương thức trợ giúp. Trong Java và .NET, đây là ToString()phương thức. Chúng tôi, các nhà phát triển JS đã quá phụ thuộc vào những thứ "chỉ hoạt động"! Ngoài ra, một người sẽ có thể nhanh chóng switchtrên một enum. So sánh chuỗi chậm hơn so với so sánh số, vì vậy bạn sẽ có switchhiệu suất kém hơn một chút nếu bạn sử dụng chuỗi thay vì số nguyên.
Rabadash8820

501

CẬP NHẬT

Cảm ơn tất cả những người ủng hộ tất cả mọi người, nhưng tôi không nghĩ câu trả lời của tôi dưới đây là cách tốt nhất để viết enum trong JavaScript nữa. Xem bài đăng trên blog của tôi để biết thêm chi tiết: Enums in JavaScript .


Thông báo tên đã có thể:

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

Ngoài ra, bạn có thể tạo các giá trị đối tượng, vì vậy bạn có thể có bánh và ăn nó:

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

Trong JavaScript, vì là ngôn ngữ động, thậm chí có thể thêm các giá trị enum vào tập sau:

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

Hãy nhớ rằng, các trường của enum (giá trị, tên và mã trong ví dụ này) không cần thiết cho việc kiểm tra danh tính và chỉ ở đó để thuận tiện. Ngoài ra, tên của thuộc tính kích thước không cần phải được mã hóa cứng, nhưng cũng có thể được đặt tự động. Vì vậy, giả sử bạn chỉ biết tên cho giá trị enum mới của mình, bạn vẫn có thể thêm nó mà không gặp vấn đề gì:

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

Tất nhiên điều này có nghĩa là một số giả định không còn có thể được thực hiện (giá trị đó đại diện cho thứ tự chính xác cho kích thước chẳng hạn).

Hãy nhớ rằng, trong JavaScript một đối tượng giống như bảng bản đồ hoặc bảng băm . Một tập hợp các cặp tên-giá trị. Bạn có thể lặp qua chúng hoặc thao túng chúng mà không cần biết nhiều về chúng trước.

Thí dụ

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

Và nhân tiện, nếu bạn quan tâm đến các không gian tên, bạn có thể muốn xem giải pháp của tôi về quản lý phụ thuộc và không gian tên đơn giản nhưng mạnh mẽ cho JavaScript: Gói JS


Vì vậy, làm thế nào bạn sẽ đi và tạo ra một KÍCH THƯỚC nếu bạn chỉ có tên của nó?
Johanisma

2
@Johanisma: Trường hợp sử dụng đó không thực sự có ý nghĩa đối với enums vì toàn bộ ý tưởng của chúng là bạn biết tất cả các giá trị trước. Tuy nhiên, không có gì ngăn bạn thêm các giá trị bổ sung sau này trong Javascript. Tôi sẽ thêm một ví dụ về câu trả lời của tôi.
Stijn de Witt

2
+1 cho liên kết đến bài đăng của bạn với cách tiếp cận thuộc tính. Thanh lịch ở chỗ các khai báo cơ bản là đơn giản, như trong OP, với tính năng thuộc tính được thêm vào khi muốn.
goodeye

@Stijin, thực sự thích giải pháp cập nhật của bạn. Mã được đăng trong các bình luận trên blog của bạn và như một bình luận dưới đây. Về cơ bản, sử dụng hàm, thực hiện xây dựng thuộc tính từ danh sách băm hiện có và tùy ý đóng băng nó (mkenum_2 trong danh sách của tôi). Chúc mừng.
Andrew Philips

Ngoài ra còn có một thư viện thực hiện nó, cũng bao gồm các tính năng hay để so sánh và tìm kiếm đảo ngược: github.com/adrai/enum
Roman M

83

Điểm mấu chốt: Bạn không thể.

Bạn có thể giả mạo nó, nhưng bạn sẽ không nhận được loại an toàn. Thông thường, điều này được thực hiện bằng cách tạo một từ điển đơn giản các giá trị chuỗi được ánh xạ tới các giá trị nguyên. Ví dụ:

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

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

Vấn đề với phương pháp này? Bạn có thể vô tình xác định lại số liệt kê của mình hoặc vô tình có các giá trị liệt kê trùng lặp. Ví dụ:

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

Biên tập

Thế còn Object.freeze của Artur Czajka thì sao? Điều đó có hiệu quả để ngăn bạn đặt thứ hai đến thứ năm không? - Chiên Quad

Hoàn toàn, Object.freezesẽ hoàn toàn khắc phục vấn đề tôi phàn nàn. Tôi muốn nhắc nhở mọi người rằng khi tôi viết những điều trên, Object.freezekhông thực sự tồn tại.

Bây giờ .... bây giờ nó mở ra một số khả năng rất thú vị.

Chỉnh sửa 2
Đây là một thư viện rất tốt để tạo enum.

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

Mặc dù nó có thể không phù hợp với mọi cách sử dụng enum hợp lệ, nhưng nó đi một chặng đường rất dài.


103
Có loại an toàn trong javascript?
Scott Evernden

3
Vì vậy, không ánh xạ giá trị đến các thuộc tính đối tượng. Sử dụng getter để truy cập liệt kê (được lưu trữ dưới dạng một thuộc tính của đối tượng "riêng tư"). Một triển khai ngây thơ sẽ giống như -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax

2
@Scott Evernden: điểm lấy. @ Khangax: vấn đề là nó vẫn là một hack. Enums đơn giản là không tồn tại trong Javascript, giai đoạn, kết thúc của câu chuyện. Ngay cả mô hình được đề xuất bởi Tim Sylvester vẫn là một hack ít hơn lý tưởng.
Randolpho

2
Rắc mã bằng chữ không phải là rất duy trì để có ý nghĩa để tạo hằng số cho nó. Tất nhiên Javascript cũng không có hằng số. Vì vậy, về cơ bản đây chỉ là một cách để viết mã sạch. Nó không thể được thi hành, nhưng Javascript không nhiều. Bạn có thể định nghĩa lại hằng, hoặc hàm hoặc hầu hết mọi thứ. EG: document.getEuityById = function () {alert ("Bạn bị vặn. Javascript không phải là loại an toàn.");};
Stijn de Witt

3
@Randolpho: Thế còn Object.freeze của Artur Czajka thì sao? Điều đó có hiệu quả để ngăn bạn đặt thứ hai đến thứ năm không?
Michael - Clay Shirky ở đâu

56

Đây là những gì tất cả chúng ta muốn:

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

Bây giờ bạn có thể tạo enum của bạn:

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

Bằng cách này, các hằng số có thể được kích hoạt theo cách thông thường (YesNo.YES, Color.GREEN) và chúng nhận được một giá trị int tuần tự (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).

Bạn cũng có thể thêm các phương thức, bằng cách sử dụng 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 */
};


Chỉnh sửa - cải thiện nhỏ - hiện có varargs: (tiếc là nó không hoạt động đúng trên IE: S ... nên gắn bó với phiên bản trước đó)

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

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

Yêu sự đơn giản của câu trả lời này!
Marquizzo

@Marquizzo (và OP) Tôi đã tạo một phiên bản cải tiến dựa trên câu trả lời này: stackoverflow.com/a/60309416/1599699
Andrew

53

Trong hầu hết các trình duyệt hiện đại, có một kiểu dữ liệu nguyên thủy biểu tượng có thể được sử dụng để tạo ra một phép liệt kê. Nó sẽ đảm bảo an toàn loại enum vì mỗi giá trị ký hiệu được JavaScript đảm bảo là duy nhất, nghĩa là Symbol() != Symbol(). Ví dụ:

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

Để đơn giản hóa việc gỡ lỗi, bạn có thể thêm một mô tả cho các giá trị enum:

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

Bản demo của Plunker

Trên GitHub, bạn có thể tìm thấy trình bao bọc đơn giản hóa mã cần thiết để khởi tạo 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

Đây là câu trả lời đúng trong lý thuyết. Trong thực tế, hỗ trợ trình duyệt 2015 là không đủ. Không sẵn sàng sản xuất cho đến nay.
vbraun

1
Mặc dù chưa có hỗ trợ trình duyệt, nhưng đây là câu trả lời tốt nhất vì nó gần với Symbolmục đích của nó.
rvighne

2
Mặc dù vậy, các giá trị enum thường cần phải được tuần tự hóa và các Biểu tượng không tiện dụng để tuần tự hóa và giải tuần tự hóa.
Andy

3
Có phải chỉ mình tôi hay Object.freezechỉ dành cho những người chưa chấp nhận thực tế rằng "trò lừa đảo có nguy cơ của riêng bạn" là hợp đồng xã hội của JS?
Andy

@Andy có serialization là phiền phức. toJSON
Cuối

30

𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲

Hãy để chúng tôi cắt thẳng vào vấn đề: kích thước tập tin. Tất cả các câu trả lời khác được liệt kê ở đây nở mã của bạn đến cực độ. Tôi trình bày với bạn rằng để có hiệu suất tốt nhất có thể, khả năng đọc mã, quản lý dự án quy mô lớn, gợi ý cú pháp trong nhiều trình soạn thảo mã và giảm kích thước mã bằng cách rút gọn, đây là cách chính xác để thực hiện liệt kê: biến ký hiệu gạch dưới.


www

Các ký hiệu gạch dưới

Như đã trình bày trong biểu đồ trên và ví dụ bên dưới, đây là năm bước dễ dàng để bắt đầu:

  1. Xác định tên cho nhóm liệt kê. Hãy nghĩ về một danh từ có thể mô tả mục đích của bảng liệt kê hoặc ít nhất là các mục trong bảng liệt kê. Ví dụ: một nhóm các bảng liệt kê màu sắc mà người dùng có thể lựa chọn có thể được đặt tên tốt hơn MÀU SẮC hơn MÀU SẮC.
  2. Quyết định xem các liệt kê trong nhóm là loại trừ lẫn nhau hay độc lập. Nếu loại trừ lẫn nhau, hãy bắt đầu mỗi tên biến được liệt kê với ENUM_. Nếu độc lập hoặc cạnh nhau, sử dụng INDEX_.
  3. Đối với mỗi mục nhập, hãy tạo một biến cục bộ mới có tên bắt đầu bằng ENUM_hoặc INDEX_sau đó là tên của nhóm, sau đó là dấu gạch dưới, sau đó là tên thân thiện duy nhất cho thuộc tính
  4. Thêm một ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, hoặc INDEXLEN_(cho dù LEN_hoặc LENGTH_là sở thích cá nhân) biến được liệt kê vào cuối. Bạn nên sử dụng biến này bất cứ nơi nào có thể trong mã của bạn để đảm bảo rằng việc thêm một mục nhập vào bảng liệt kê và tăng giá trị này sẽ không phá vỡ mã của bạn.
  5. Cung cấp cho mỗi biến liệt kê liên tiếp một giá trị một nhiều hơn trước, bắt đầu từ 0. Có ý kiến trên trang này mà nói 0không nên được sử dụng như một giá trị được liệt kê bởi vì 0 == null, 0 == false, 0 == "", và sự điên loạn JS khác. Tôi gửi cho bạn rằng, để tránh vấn đề này và tăng hiệu suất cùng một lúc, luôn luôn sử dụng ===và không bao giờ để ==xuất hiện trong mã của bạn ngoại trừ với typeof(ví dụ typeof X == "string"). Trong tất cả các năm sử dụng ===, tôi chưa bao giờ gặp vấn đề với việc sử dụng 0 làm giá trị liệt kê. Nếu bạn vẫn còn vuông vức, thì 1có thể được sử dụng làm giá trị bắt đầu trong ENUM_bảng liệt kê (nhưng không phải trong INDEX_bảng liệt kê) mà không bị phạt hiệu suất trong nhiều trường hợp.
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
}

Đây là cách tôi nhớ khi nào nên sử dụng INDEX_và khi nào nên sử dụng ENUM_:

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

Tuy nhiên, ENUM_trong một số trường hợp, có thể thích hợp làm chỉ số như khi đếm số lần xuất hiện của từng mục.

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

Quan sát rằng, trong đoạn mã trên, thật dễ dàng để thêm một loại thú cưng mới: bạn sẽ phải nối thêm một mục mới sau đó ENUM_PET_RATvà cập nhật ENUMLEN_PETtương ứng. Có thể khó khăn hơn và có lỗi khi thêm một mục mới trong các hệ thống liệt kê khác.


wvvvvvvvvvv v tuần năm tuần năm năm nay năm nay năm tuần năm năm nay năm nay năm tuần năm năm ngày thứ năm mùa thứ năm mùa thứ năm mùa năm mùa năm tuần thứ năm thứ năm mùa chiều sáng sóng sáng thứ năm tuần thứ năm thứ năm thứ năm thứ năm năm nay năm nay năm nay năm nay

𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀 𝗪𝗶𝘁𝗵

Ngoài ra, cú pháp liệt kê này cho phép mở rộng lớp rõ ràng và súc tích như được thấy dưới đây. Để mở rộng một lớp, thêm một số tăng vào LEN_mục nhập của lớp cha. Sau đó, kết thúc lớp con với LEN_mục nhập riêng để lớp con có thể được mở rộng hơn nữa trong tương lai.

Sơ đồ mở rộng bổ sung

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

(Độ dài: 2.450 byte)

Một số người có thể nói rằng điều này ít thực tế hơn các giải pháp khác: nó mất rất nhiều không gian, mất nhiều thời gian để viết và nó không được phủ bằng cú pháp đường. Những người đó sẽ đúng nếu họ không giảm thiểu mã của họ. Tuy nhiên, không có người hợp lý sẽ để lại mã chưa được tối ưu hóa trong sản phẩm cuối cùng. Đối với việc thu nhỏ này, Trình biên dịch đóng cửa là trình biên dịch tốt nhất mà tôi chưa tìm thấy. Truy cập trực tuyến có thể được tìm thấy ở đây . Trình biên dịch đóng có thể lấy tất cả dữ liệu liệt kê này và nội tuyến nó, làm cho Javascript của bạn trở nên siêu nhỏ và chạy siêu nhanh. Do đó, Minify với Trình biên dịch đóng. Quan sát.


wvvvvvvvvvv v tuần năm tuần năm năm nay năm nay năm tuần năm năm nay năm nay năm tuần năm năm ngày thứ năm mùa thứ năm mùa thứ năm mùa năm mùa năm tuần thứ năm thứ năm mùa chiều sáng sóng sáng thứ năm tuần thứ năm thứ năm thứ năm thứ năm năm nay năm nay năm nay năm nay

𝗪𝗶𝘁𝗵 𝗖𝗹𝗼𝘀𝘂𝗿𝗲

Trình biên dịch đóng có thể thực hiện một số tối ưu hóa đáng kinh ngạc thông qua các suy luận vượt quá khả năng của bất kỳ công cụ khai thác Javascript nào khác. Trình biên dịch đóng cửa có thể đặt các biến nguyên thủy nội tuyến thành một giá trị cố định. Trình biên dịch đóng cửa cũng có thể thực hiện các suy luận dựa trên các giá trị được nội tuyến này và loại bỏ các khối không được sử dụng trong câu lệnh if và vòng lặp.

Mã vắt thông qua Trình biên dịch đóng

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

(Độ dài: 605 byte)

Trình biên dịch đóng cửa thưởng cho bạn mã hóa thông minh hơn và tổ chức mã của bạn tốt bởi vì, trong khi nhiều công cụ khai thác trừng phạt mã có tổ chức với kích thước tệp được rút gọn lớn hơn, thì Trình biên dịch đóng gói có thể sàng lọc tất cả sự sạch sẽ và tỉnh táo của bạn để tạo ra kích thước tệp thậm chí nhỏ hơn nếu bạn sử dụng các thủ thuật như liệt kê tên biến. Điều đó, trong suy nghĩ này, là chén thánh của mã hóa: một công cụ vừa hỗ trợ mã của bạn với kích thước nhỏ hơn vừa phải và hỗ trợ tâm trí của bạn bằng cách rèn luyện thói quen lập trình tốt hơn.


wvvvvvvvvvv v tuần năm tuần năm năm nay năm nay năm tuần năm năm nay năm nay năm tuần năm năm ngày thứ năm mùa thứ năm mùa thứ năm mùa năm mùa năm tuần thứ năm thứ năm mùa chiều sáng sóng sáng thứ năm tuần thứ năm thứ năm thứ năm thứ năm năm nay năm nay năm nay năm nay

𝗖𝗼𝗱𝗲

Bây giờ, chúng ta hãy xem tệp tương đương sẽ lớn như thế nào nếu không có bất kỳ bảng liệt kê nào.

Nguồn không sử dụng liệt kê (độ dài: 1.973 byte (ngắn hơn 477 byte so với mã liệt kê!))
Được rút gọn mà không sử dụng liệt kê (độ dài: 843 byte ( dài hơn 238 byte so với mã liệt kê ))

Biểu đồ kích thước mã



Như đã thấy, không có bảng liệt kê, mã nguồn ngắn hơn với chi phí của mã rút gọn lớn hơn. Tôi không biết gì về bạn; nhưng tôi biết chắc chắn rằng tôi không kết hợp mã nguồn vào sản phẩm cuối cùng. Do đó, hình thức liệt kê này vượt trội hơn nhiều so với kết quả là kích thước tệp được rút gọn nhỏ hơn.


wvvvvvvvvvv v tuần năm tuần năm năm nay năm nay năm tuần năm năm nay năm nay năm tuần năm năm ngày thứ năm mùa thứ năm mùa thứ năm mùa năm mùa năm tuần thứ năm thứ năm mùa chiều sáng sóng sáng thứ năm tuần thứ năm thứ năm thứ năm thứ năm năm nay năm nay năm nay năm nay

🤝 𝗕𝘂𝗴

Một ưu điểm khác về hình thức liệt kê này là nó có thể được sử dụng để dễ dàng quản lý các dự án quy mô lớn mà không phải hy sinh kích thước mã được rút gọn. Khi làm việc trong một dự án lớn với nhiều người khác, có thể có ích khi đánh dấu và gắn nhãn rõ ràng tên biến với người đã tạo mã để người tạo mã gốc có thể nhanh chóng được xác định để sửa lỗi cộng tác.

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

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

Hơn nữa, hình thức liệt kê này cũng nhanh hơn nhiều sau khi thu nhỏ. Trong các thuộc tính được đặt tên bình thường, trình duyệt phải sử dụng các hashtag để tìm kiếm vị trí của thuộc tính trên đối tượng. Mặc dù trình biên dịch JIT lưu trữ thông minh vị trí này trên đối tượng một cách thông minh, nhưng vẫn có chi phí rất lớn do các trường hợp đặc biệt như xóa một thuộc tính thấp hơn khỏi đối tượng.

Nhưng, với các mảng PACKED_ELEMENTS được lập chỉ mục số nguyên không thưa thớt liên tục , trình duyệt có thể bỏ qua phần lớn chi phí đó vì chỉ mục của giá trị trong mảng bên trong đã được chỉ định. Có, theo tiêu chuẩn ECMAScript, tất cả các thuộc tính được coi là được coi là chuỗi. Tuy nhiên, khía cạnh này của tiêu chuẩn ECMAScript rất sai lệch về hiệu suất vì tất cả các trình duyệt có tối ưu hóa đặc biệt cho các chỉ mục số trong mảng.

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

So sánh mã trên với mã dưới đây.

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

Người ta có thể phản đối mã với các liệt kê dường như dài hơn nhiều so với mã với các đối tượng thông thường, nhưng ngoại hình có thể bị đánh lừa. Điều quan trọng cần nhớ là kích thước mã nguồn không tỷ lệ với kích thước đầu ra khi sử dụng Trình biên dịch đóng cửa sử thi. Quan sát.

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

Mã rút gọn không có bảng liệt kê ở trên và mã được rút gọn với bảng liệt kê ở dưới.

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

Ví dụ trên chứng minh rằng, ngoài việc có hiệu suất vượt trội, mã được liệt kê còn dẫn đến kích thước tệp được thu nhỏ hơn.


wvvvvvvvvvv v tuần năm tuần năm năm nay năm nay năm tuần năm năm nay năm nay năm tuần năm năm ngày thứ năm mùa thứ năm mùa thứ năm mùa năm mùa năm tuần thứ năm thứ năm mùa chiều sáng sóng sáng thứ năm tuần thứ năm thứ năm thứ năm thứ năm năm nay năm nay năm nay năm nay

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

Hơn nữa, anh đào cá nhân trên đỉnh này đang sử dụng hình thức liệt kê này cùng với trình soạn thảo văn bản CodeMirror ở chế độ Javascript. Chế độ tô sáng cú pháp Javascript của CodeMirror làm nổi bật các biến cục bộ trong phạm vi hiện tại. Bằng cách đó, bạn biết ngay lập tức khi bạn nhập tên biến chính xác bởi vì nếu tên biến được khai báo trước đó bằng vartừ khóa, thì tên biến sẽ chuyển sang màu đặc biệt (màu lục lam theo mặc định). Ngay cả khi bạn không sử dụng CodeMirror, thì ít nhất trình duyệt cũng sẽ hữu ích[variable name] is not definedngoại lệ khi thực thi mã với tên liệt kê sai. Ngoài ra, các công cụ JavaScript như JSLint và Trình biên dịch đóng cửa rất lớn về việc cho bạn biết khi bạn nhập sai tên biến liệt kê. CodeMirror, trình duyệt và các công cụ Javascript khác nhau kết hợp lại giúp việc gỡ lỗi hình thức liệt kê này rất đơn giản và thực sự dễ dàng.

Trình diễn nổi bật 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
}

Trong đoạn trích trên, bạn đã được thông báo lỗi vì ENUM_COLORENUM_DNEkhông tồn tại.


wvvvvvvvvvv v tuần năm tuần năm năm nay năm nay năm tuần năm năm nay năm nay năm tuần năm năm ngày thứ năm mùa thứ năm mùa thứ năm mùa năm mùa năm tuần thứ năm thứ năm mùa chiều sáng sóng sáng thứ năm tuần thứ năm thứ năm thứ năm thứ năm năm nay năm nay năm nay năm nay

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Tôi nghĩ thật an toàn khi nói rằng phương pháp liệt kê này thực sự là cách tốt nhất để đi không chỉ cho kích thước mã được rút gọn, mà còn cho hiệu suất, gỡ lỗi và cộng tác.

Sau khi đọc một câu hỏi hữu ích, tôi cảm ơn tác giả đã đổ thời gian vào bài viết của họ bằng cách nhấp vào mũi tên trên cùng bên trái trong hộp câu hỏi. Mỗi hộp trả lời cũng có một trong những mũi tên hướng lên.


Hở. Tôi đặc biệt thích khả năng đọc và dễ sử dụng và hiểu kích thước mã.
Andrew

1
@Andrew Với câu trả lời của tôi, bạn có thể có cả hai. Câu trả lời của tôi cho kết quả dễ sử dụng / quản lý mã nhất và ở kích thước mã được rút gọn nhỏ nhất.🙂
Jack Giffin

1
@Andrew Tôi đã cố gắng áp dụng Yet Another Enum (YEA!) Cho ví dụ về trình phân tích màu trong câu trả lời của tôi. Tuy nhiên, tôi đã tìm thấy một số vấn đề mà bạn có thể muốn giải quyết. YEA không có cách nào để mở rộng bảng liệt kê với các lớp con, buộc tôi phải tạo các lớp cha và con riêng biệt, điều này có thể khá khó để quản lý trong các dự án lớn. YEA không đảm bảo mục nhập tồn tại ( colors.REEDsản lượng cũ undefined), do đó lỗi chính tả tạo ra những câu hỏi hóc búa. YEA không phân biệt giữa việc sử dụng bảng liệt kê dưới dạng chỉ mục và ID, dẫn đến mã khó hiểu khi mọi thứ trông giống nhau. Tiết
Jack Giffin

1
@Andrew đào YEA cản trở khả năng thu nhỏ của Compiler. So sánh mã nguồn với YEA (3549 byte) với mã được rút gọn với YEA (1344 byte) với mã được rút gọn với giải pháp của tôi (604 byte). Cuối cùng, YEA liên quan đến "ánh xạ theo tên" vì nó tách tên chuỗi khỏi ID được liệt kê. Của tôi chỉ xem xét ID, do đó không cần "ánh xạ theo tên", dẫn đến thiết kế đơn giản hơn và hiệu suất tốt hơn. Cảm ơn bạn đã chia sẻ giải pháp của bạn, nhưng nó cần nhiều sửa chữa trước khi nó có thể thực tế.
Jack Giffin

1
@Andrew Bạn có quyền với ý kiến ​​của bạn khi tôi là của tôi
Jack Giffin

23

Tôi đã chơi xung quanh với điều này, vì tôi yêu enum của tôi. =)

Sử dụng Object.definePropertytôi nghĩ rằng tôi đã đưa ra một giải pháp khả thi.

Đây là một jsfiddle: http://jsfiddle.net/ZV4A6/

Sử dụng phương pháp này .. về lý thuyết, bạn có thể gọi và định nghĩa các giá trị enum cho bất kỳ đối tượng nào, mà không ảnh hưởng đến các thuộc tính khác của đối tượng đó.

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

Vì thuộc tính writable:falsenày nên làm cho nó an toàn.

Vì vậy, bạn sẽ có thể tạo một đối tượng tùy chỉnh, sau đó gọi Enum()nó. Các giá trị được gán bắt đầu từ 0 và tăng trên mỗi mục.

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

3
Nếu bạn thêm return this; vào cuối Enum, bạn có thể làm:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

Tôi đã không cân nhắc điều đó, vì đó không phải là phương pháp làm việc bình thường của tôi. Nhưng bạn hoàn toàn chính xác! Tôi sẽ chỉnh sửa nó trong.
Duncan

Tôi thực sự thích điều này mặc dù tôi không phải là một fan hâm mộ lớn của việc thu thập không gian Object (với chức năng toàn cầu ENUM). Chuyển đổi hàm này thành hàm mkenum và thêm các phép gán số tùy chọn => var hỗn hợp = mkenum ('BLACK', {RED: 0x0F00, BLUE: 0X0F, XANH: 0x0F0, WHITE: 0x0FFF, ONE: 1}, TWO, BA ; // Thêm mã của tôi dưới dạng câu trả lời bên dưới. Cảm ơn.
Andrew Philips

Thành thật mà nói, tôi thậm chí không sử dụng nó nữa. Tôi đã sử dụng Trình biên dịch đóng cửa của Google và điều này không hoạt động tốt (hoặc nó chỉ làm phức tạp mọi thứ) nếu bạn sử dụng cài đặt Nâng cao. Vì vậy, tôi vừa trở lại ký hiệu đối tượng tiêu chuẩn.
Duncan

1
falselà mặc định cho writable, enumerableconfigurable. Không cần phải nhai qua mặc định.
ceving

23

Sử dụng proxy Javascript

TLDR: Thêm lớp này vào các phương thức tiện ích của bạn và sử dụng nó trong toàn bộ mã của bạn, nó chế giễu hành vi của Enum từ các ngôn ngữ lập trình truyền thống và thực sự gây ra lỗi khi bạn cố truy cập vào một điều tra viên không tồn tại hoặc thêm / cập nhật một điều tra viên. Không cần phải dựa vào 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);
  }
}

Sau đó tạo enum bằng cách khởi tạo lớp:

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

Giải thích đầy đủ:

Một tính năng rất có lợi của Enums mà bạn nhận được từ các ngôn ngữ truyền thống là chúng nổ tung (gây ra lỗi thời gian biên dịch) nếu bạn cố truy cập vào một điều tra viên không tồn tại.

Bên cạnh việc đóng băng cấu trúc enum bị chế giễu để ngăn các giá trị bổ sung vô tình / độc hại được thêm vào, không có câu trả lời nào khác giải quyết tính năng nội tại của Enums.

Như bạn có thể biết, việc truy cập các thành viên không tồn tại trong JavaScript chỉ đơn giản là trả về undefinedvà không làm nổ mã của bạn. Vì các điều tra viên là các hằng số được xác định trước (tức là các ngày trong tuần), nên không bao giờ có trường hợp khi một điều tra viên không được xác định.

Đừng hiểu sai ý tôi, hành vi trở lại của JavaScript undefinedkhi truy cập các thuộc tính không xác định thực sự là một tính năng rất mạnh mẽ của ngôn ngữ, nhưng đó không phải là một tính năng bạn muốn khi bạn đang cố gắng giả lập các cấu trúc Enum truyền thống.

Đây là nơi các đối tượng Proxy tỏa sáng. Proxy đã được chuẩn hóa trong ngôn ngữ với sự ra đời của ES6 (ES2015). Đây là mô tả từ MDN:

Đối tượng Proxy được sử dụng để xác định hành vi tùy chỉnh cho các hoạt động cơ bản (ví dụ: tra cứu thuộc tính, gán, liệt kê, gọi hàm, v.v.).

Tương tự như proxy máy chủ web, proxy JavaScript có thể chặn các hoạt động trên các đối tượng (với việc sử dụng "bẫy", gọi chúng là hook nếu bạn muốn) và cho phép bạn thực hiện các kiểm tra, hành động và / hoặc thao tác khác nhau trước khi hoàn thành (hoặc trong một số trường hợp dừng các hoạt động hoàn toàn, đó chính xác là những gì chúng ta muốn làm nếu và khi chúng ta cố gắng tham chiếu một điều tra viên không tồn tại).

Đây là một ví dụ giả định sử dụng đối tượng Proxy để bắt chước Enums. Các điều tra viên trong ví dụ này là các Phương thức HTTP tiêu chuẩn (nghĩa là "NHẬN", "BÀI", v.v.):

// 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: cái quái gì là một proxy?

Tôi nhớ khi tôi lần đầu tiên bắt đầu nhìn thấy từ proxy ở khắp mọi nơi, nó chắc chắn không có ý nghĩa với tôi trong một thời gian dài. Nếu đó là bạn ngay bây giờ, tôi nghĩ một cách dễ dàng để khái quát hóa proxy là nghĩ về chúng như phần mềm, tổ chức hoặc thậm chí là những người đóng vai trò trung gian hoặc trung gian giữa hai máy chủ, công ty hoặc người.


Làm thế nào để làm một cái gì đó như myEnum.valueOf ("someStringValue")? Dự kiến: trong trường hợp chuỗi đầu vào có giá trị của một phần tử của điều tra, sẽ trả về mục. Trong trường hợp không có mục nào có giá trị chuỗi đó, hãy ném ngoại lệ.
sscarduzio

@sscarduzio bạn có thể ghi đè valueOfphương thức mặc định bằng cách chỉ định nó làm phương thức ví dụ trên lớp Enum. Tuy nhiên, tại sao bạn muốn truy cập nó theo cách này thay vì chỉ truy cập nó thông qua ký hiệu chấm?
Govind Rai

Enum của tôi là const logLevelEnum = new Enum ({INFO: "thông tin", DEBUG: "gỡ lỗi"}) và tôi phân tích từ đầu vào một chuỗi "thông tin" hoặc "gỡ lỗi" tùy ý. Vì vậy, tôi cần một cái gì đó như currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio

1
Tại sao bạn không thể làm gì logLevelEnum[settings.get("log_level")]? việc thêm parseOrThrowsẽ chỉ lặp đi lặp lại với những gì bẫy proxy đang làm cho bạn.
Govind Rai

17

Đây là một cái cũ tôi biết, nhưng cách nó đã được thực hiện thông qua giao diện TypeScript là:

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

Điều này cho phép bạn tra cứu cả hai MyEnum.Bartrả về 1 và MyEnum[1]trả về "Bar" bất kể thứ tự khai báo.


1
Plus MyEnum ["Bar"] hoạt động trả về 1 ... <3 TypeScript cho đến nay ...
David Karlaš

3
và tất nhiên nếu bạn thực sự đang sử dụng Bản đánh máy:enum MyEnum { Foo, Bar, Foobar }
Quốc hội

16

Trong ES7 , bạn có thể thực hiện ENUM thanh lịch dựa trên các thuộc tính tĩnh:

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

sau đó

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

Ưu điểm (của việc sử dụng lớp thay vì đối tượng bằng chữ) là có một lớp cha, Enumsau đó tất cả các Enums của bạn sẽ mở rộng lớp đó.

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

4
Bạn có thể giải thích tại sao có một lớp cha mẹ là một lợi thế, xin vui lòng? Tôi cảm thấy như mình đang thiếu thứ gì đó!
Jon G

7
Đừng làm vậy. new ColorEnum()làm cho hoàn toàn không có ý nghĩa.
Bergi

3
kéo dài một enum nghe có vẻ điên rồ, thực sự
Codii

một khi ngôn ngữ không hỗ trợ nó thực sự sẽ có ý nghĩa giữ quy ước này và sử dụng như thế này! tôi đồng ý!
xpto

Tôi nghĩ (?) Những gì OP đang có, là: Lợi ích của tĩnh thuần là nó có sẵn ở mọi nơi dưới dạng đơn lẻ và bạn không cần phải khởi tạo lớp - OP không gợi ý rằng bạn làm thế! Tôi nghĩ rằng những gì ông nói là lớp cha Enumcó tiêu chuẩn tĩnh phương pháp điều tra viên vào nó, giống như getValues(), getNames(), iterate(), vv Nếu đó là trường hợp, bạn không cần phải reimplement chúng đối với từng loại mới enum.
Kỹ sư

15

Đây là giải pháp mà tôi sử dụng.

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

Và bạn xác định enum của bạn như thế này:

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

Và đây là cách bạn truy cập enum của bạn:

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

Tôi thường sử dụng 2 phương thức cuối cùng để ánh xạ enum từ các đối tượng tin nhắn.

Một số ưu điểm của phương pháp này:

  • Dễ dàng khai báo
  • Dễ dàng truy cập enum của bạn
  • Enum của bạn có thể là loại phức tạp
  • Lớp Enum có một số bộ nhớ đệm kết hợp nếu bạn đang sử dụng getByValue rất nhiều

Một số nhược điểm:

  • Một số quản lý bộ nhớ lộn xộn đang diễn ra ở đó, khi tôi giữ các tài liệu tham khảo cho enums
  • Vẫn không có loại an toàn

14

Tạo một đối tượng theo nghĩa đen:

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

12
constkhông làm cho các thuộc tính của đối tượng trở nên bất biến, điều đó chỉ có nghĩa là biến Modeskhông thể được gán lại cho thứ khác. Để làm cho nó đầy đủ hơn, sử dụng Object.freeze()cùng với const.
rvighne

Xin vui lòng không sử dụng Object.freeze. Nó ngăn chặn Trình biên dịch đóng cửa nội tuyến đối tượng.
Jack Giffin

11

Nếu bạn đang sử dụng Backbone , bạn có thể nhận được chức năng enum toàn diện (tìm theo id, tên, thành viên tùy chỉnh) miễn phí bằng 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

câu trả lời của bạn quá phức tạp

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 Tôi đồng ý rằng câu trả lời của bạn có hiệu suất cao hơn và câu trả lời của tôi có thể chiếm nhiều bộ nhớ hơn, mặc dù bạn không nên cho rằng mọi người đều muốn enum theo cách C ++ thực hiện nó. Vui lòng tôn trọng các câu trả lời khác và các nhà phát triển có thể thích câu trả lời này hơn câu trả lời của bạn.
Xeltor

7

Tôi đã sửa đổi giải pháp của 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);
    }
  }

Kiểm tra:

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

Tôi đã đưa ra này tiếp cận được mô hình hóa sau khi enums trong Java. Đây là loại an toàn, và vì vậy bạn cũng có thể thực hiện instanceofkiểm tra.

Bạn có thể định nghĩa enums như thế này:

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

Daysbây giờ đề cập đến 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"

Việc thực hiện:

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
    }

})();

Có vẻ tốt, có lẽ bạn nên kiểm tra sự tồn tại của freezephương thức để tương thích ngược? Ví dụ:if (Object.freeze) { Object.freeze(values); }
FBB

Điểm tốt! Sẽ làm!
Vivin Paliath

6

IE8 không hỗ trợ phương thức đóng băng ().
Nguồn: http : // Khangax.github.io/compat-table/es5/ , Nhấp vào "Hiển thị trình duyệt lỗi thời?" trên cùng, và kiểm tra IE8 & đóng băng giao lộ col.

Trong dự án trò chơi hiện tại của tôi, tôi đã sử dụng bên dưới, vì rất ít khách hàng vẫn sử dụng IE8:

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

Chúng tôi cũng có thể làm:

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

hoặc thậm chí này:

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

Cái cuối cùng, có vẻ hiệu quả nhất đối với chuỗi, nó làm giảm tổng băng thông của bạn nếu bạn có máy chủ và máy khách trao đổi dữ liệu này.
Tất nhiên, giờ đây, nhiệm vụ của bạn là đảm bảo không có xung đột trong dữ liệu (RE, EX, v.v. phải là duy nhất, 1, 2, v.v. phải là duy nhất). Lưu ý rằng bạn cần duy trì những thứ này mãi mãi để tương thích ngược.

Bài tập:

var wildType = CONST_WILD_TYPES.REGULAR;

So sánh:

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

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

Bạn không cần đảm bảo rằng bạn không gán các số trùng lặp cho các giá trị enum khác nhau theo cách này. Một đối tượng mới được khởi tạo và gán cho tất cả các giá trị enum.


Câu trả lời này được đánh giá thấp. Đó là một trong những ý tưởng yêu thích của tôi vì sự đơn giản của nó. Trong thực tế, tôi nghĩ rằng tôi sẽ gắn bó với chuỗi vì bây giờ việc gỡ lỗi dễ dàng hơn.
Domino

Hmm, chỉ cần đảm bảo rằng mã này không được gọi hai lần ...
Andrew

4

Đây là một vài cách khác nhau để thực hiện các enum TypeScript .

Cách dễ nhất là chỉ lặp qua một đối tượng, thêm các cặp khóa-giá trị đảo ngược vào đối tượng. Hạn chế duy nhất là bạn phải tự đặt giá trị cho từng thành viên.

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


Và đây là một mixin lodash để tạo enum bằng chuỗi. Mặc dù phiên bản này có liên quan nhiều hơn một chút, nhưng nó sẽ tự động đánh số cho bạn. Tất cả các phương thức lodash được sử dụng trong ví dụ này đều có tương đương JavaScript thông thường, vì vậy bạn có thể dễ dàng chuyển chúng ra nếu muốn.

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

rất thông minh, cảm ơn
Ilan

4

Tôi vừa xuất bản gói NPM gen_enum cho phép bạn nhanh chóng tạo cấu trúc dữ liệu Enum trong 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 

Một điều thú vị về công cụ nhỏ này là trong môi trường hiện đại (bao gồm các trình duyệt nodejs và IE 9+), đối tượng Enum được trả về là bất biến.

Để biết thêm thông tin, vui lòng kiểm tra https://github.com/greenlaw110/enumjs

Cập nhật

Tôi đã lỗi thời gen_enumgói và hợp nhất hàm vào gói constjs , cung cấp nhiều tính năng hơn bao gồm các đối tượng không thay đổi, khử tuần tự chuỗi JSON, hằng chuỗi và tạo bitmap, v.v. Thanh toán https://www.npmjs.com/package/constjs để biết thêm thông tin

Để nâng cấp từ gen_enumđể constjschỉ cần thay đổi tuyên bố

var genEnum = require('gen_enum');

đến

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

4

Giải pháp đơn giản nhất:

Tạo nên

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

Nhận giá trị

console.log(Status.Ready) // 1

Nhận chìa khóa

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

4

Tôi đã tạo một lớp Enum có thể tìm nạp các giá trị VÀ tên tại O (1). Nó cũng có thể tạo ra một Mảng đối tượng chứa tất cả Tên và Giá trị.

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

Bạn có thể khởi tạo nó như thế này:

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

Để tìm nạp một giá trị (như Enums trong C #):

var val2 = enum1.item2;

Để tìm nạp tên cho một giá trị (có thể mơ hồ khi đặt cùng một giá trị cho các tên khác nhau):

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

Để có được một mảng với mỗi tên và giá trị trong một đối tượng:

var arr = enum1.GetObjArr();

Sẽ tạo ra:

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

Bạn cũng có thể dễ dàng nhận các tùy chọn chọn html:

var html = enum1.GetSelectOptionsHTML();

Mà giữ:

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

4

Mặc dù chỉ có các phương thức tĩnh (và không phải thuộc tính tĩnh) được hỗ trợ trong ES2015 (cũng xem tại đây , §15.2.2.2), bạn có thể sử dụng cách bên dưới với Babel với es2015cài đặt sẵn:

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

Tôi thấy điều này hoạt động như mong đợi ngay cả trên các mô-đun (ví dụ: nhập CellStateenum từ mô-đun khác) và cả khi tôi nhập mô-đun bằng Webpack.

Ưu điểm phương pháp này có hơn hầu hết các câu trả lời khác là bạn có thể sử dụng nó cùng với một loại tĩnh kiểm tra (ví dụ như dòng chảy ) và bạn có thể khẳng định, tại thời gian phát triển sử dụng kiểm tra kiểu tĩnh, mà các biến của bạn, các thông số, vv là các cụ CellState" enum "thay vì một số enum khác (sẽ không thể phân biệt nếu bạn sử dụng các đối tượng hoặc ký hiệu chung).

cập nhật

Đoạn mã trên có một thiếu sót ở chỗ nó cho phép người ta tạo thêm các đối tượng loại CellState(mặc dù người ta không thể gán chúng cho các trường tĩnh CellStatekể từ khi nó bị đóng băng). Tuy nhiên, mã tinh tế hơn dưới đây cung cấp các lợi thế sau:

  1. không có nhiều đối tượng loại CellStatecó thể được tạo ra
  2. bạn được đảm bảo rằng không có hai trường hợp enum nào được gán cùng một mã
  3. phương thức tiện ích để lấy lại enum từ một chuỗi đại diện
  4. các valueshàm trả về tất cả các trường hợp của enum không nhất thiết phải tạo ra giá trị lợi nhuận trong, tay (và dễ bị lỗi) cách trên.

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

Ví dụ hay :-)
Ashraf.Shk786 17/05/18

4

cách es7, (lặp, đóng băng), cách sử dụng:

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'

mã:

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

Đây là cách typecript dịch nó enumsang Javascript:

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

Hiện nay:

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

Lúc đầu, tôi đã bối rối tại sao obj[1]trả về 'Active', nhưng sau đó nhận ra rằng đơn giản đã chết của nó - Toán tử gán gán gán giá trị và sau đó trả về nó:

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

4

Bạn có thể làm một cái gì đó như thế này

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

Như được định nghĩa trong thư viện này. https://github.com/webmodule/foo/blob/master/foo.js#L217

Ví dụ hoàn chỉnh https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026


3

Một cách nhanh chóng và đơn giản sẽ là:

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

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

6
Chức năng này là không cần thiết và cung cấp cho bạn kết quả chính xác giống như những gì OP đã đăng.
Sildoreth

3

Khi viết, tháng 10 năm 2014 - vì vậy đây là một giải pháp đương đại. Đang viết giải pháp dưới dạng Mô-đun Node và đã bao gồm một bài kiểm tra bằng Mocha và Chai, cũng như gạch dưới. Bạn có thể dễ dàng bỏ qua những điều này và chỉ cần lấy mã Enum nếu muốn.

Nhìn thấy rất nhiều bài đăng với các thư viện quá phức tạp, vv Giải pháp để nhận được sự hỗ trợ của enum trong Javascript rất đơn giản, nó thực sự không cần thiết. Đây là mã:

Tệp: 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;

Và một bài kiểm tra để minh họa những gì nó mang lại cho bạn:

tệp: 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');
        });
    });

});

Như bạn có thể thấy, bạn nhận được một nhà máy Enum, bạn có thể nhận được tất cả các khóa chỉ bằng cách gọi enum.keys và bạn có thể tự khớp các khóa với các hằng số nguyên. Và bạn có thể sử dụng lại nhà máy với các giá trị khác nhau và xuất các Enums được tạo bằng cách sử dụng phương pháp mô đun của Node.

Một lần nữa, nếu bạn chỉ là một người dùng bình thường hoặc trong trình duyệt, v.v., chỉ cần lấy phần mã của nhà máy, có khả năng xóa thư viện gạch dưới nếu bạn không muốn sử dụng nó trong mã của mình.


5
Bạn có thể đăng câu trả lời chỉ với "đây là cách để làm điều này như một người dùng bình thường, người chỉ muốn enum, không phải nhà máy, gạch dưới, hoặc bất cứ điều gì lạ mắt"?
GreenAsJade

5
Mặc dù điều này là khá tuyệt vời từ một nhà phát triển, nó không sạch sẽ hay dễ đọc. Giải pháp Enum từ OP dễ dàng hơn và dễ đọc hơn theo mọi cách, và do đó tốt hơn để sử dụng. Tuy nhiên, khá tuyệt vời khi bạn nghĩ ra điều này.
David

3

Nó dễ sử dụng, tôi nghĩ vậy. 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);

CẬP NHẬT:

Có mã trợ giúp của tôi ( TypeHelper).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.