Câu hỏi Cú pháp ưu tiên để xác định enums trong JavaScript là gì?


Cú pháp ưu tiên để xác định enums 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 là có một thành ngữ thích hợp hơn?


1678
2017-11-13 19:09


gốc


Tôi đã tham gia một thử nghiệm trên Javascript 1.8 trên odesk và họ hỏi những gì được cho là để giải quyết liệt kê trong javascript, 2 câu trả lời rõ ràng là hoặc là cho phép từ khóa hoặc máy phát điện. Tôi mơ hồ nhớ đến enums trong khóa học c ++ của tôi, và thực sự không biết những gì máy phát điện; nhưng có khả năng điều này sẽ hữu ích. - Devin G Rhode
Không sử dụng 0 dưới dạng một số đếm. Trừ khi nó được sử dụng cho cái gì đó chưa được thiết lập. Xử lý JS false || undefined || null || 0 || "" || '' || NaN tất cả đều có giá trị như nhau khi so sánh sử dụng ==. - matsko
@ matsko không phải là chỉ là một cuộc tranh luận chống lại bằng cách sử dụng ==? - sdm350
Nhưng false == 0 và +null == 0 (và chuyển đổi cho các con số xảy ra đôi khi bạn không mong đợi nó), trong khi null == undefined quá, và +undefined Là NaN (Tuy nhiên NaN != NaN). - sanderd17
Ma trận đẳng thức kép là khó hiểu hơn so với định dạng tự động của từ microsoft - aaaaaa


Các câu trả lời:


Kể từ 1.8.5 nó có thể đóng dấu và đóng băng đối tượng, do đó, xác định ở trên là:

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

hoặc là

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

và Voila! JS enums.

Tuy nhiên, điều này không ngăn cản bạn gán một 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 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 là lưu lượng.

Nguồn

Báo giá là không cần thiết nhưng tôi giữ chúng cho sự nhất quán.


541
2018-02-18 11:03



Theo Wikipedia (en.wikipedia.org/wiki/JavaScript#Versions) nó áp dụng cho Firefox 4, IE 9, Opera 11.60 và tôi biết nó hoạt động trong Chrome. - Artur Czajka
Đây là câu trả lời đúng ngay bây giờ vào năm 2012. Đơn giản hơn: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Bạn không cần phải chỉ định một 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
Để tương thích ngược, if (Object.freeze) { Object.freeze(DaysEnum); } - saluce
Tôi muốn chỉ ra rằng làm ({ monday: {},  v.v. có nghĩa là nếu bạn chuyển đổi đối tượng đó thành JSON qua chuỗi, bạn sẽ nhận được [{"day": {}}] sẽ không hoạt động. - jcollum
@Supuhstar Ý kiến ​​của tôi về câu hỏi này bây giờ là khác nhau. Không sử dụng freeze (), nó hoàn toàn vô dụng và lãng phí thời gian làm những điều "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 bình luận trước của tôi là NHIỀU HƠN THÊM so với số so sánh. - Gabriel Llamas


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

Có nói rằng, vì nó không quan trọng những gì các giá trị (bạn đã sử dụng 0, 1, 2), tôi muốn sử dụng một chuỗi có ý nghĩa trong trường hợp bạn đã bao giờ muốn đầu ra giá trị hiện tại.


583
2017-11-13 19:13



Đây là một nhà máy enum đơn giản mà tôi tạo ra; npmjs.org/package/simple-enum - Tolga E
Xin lỗi, nhưng việc triển khai enum đó có một số vấn đề. Tôi khuyên bạn nên sử dụng thay vào đó: github.com/rauschma/enums - paldepind
Đ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 bài này ở đây. Giải pháp của OP là chính xác. Nó sẽ tốt hơn, mặc dù, nếu được sử dụng với Object.freeze(). Điều này sẽ ngăn chặn các mã khác thay đổi giá trị của enum. Thí dụ: var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2}); - Sildoreth
@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ó xuống mức tối thiểu, mà còn thêm một vài tính năng! Tôi đã chia đôi của bạn và đặt tất cả ở đây: github.com/BlueHuskyStudios/Micro-JS-Enum - Supuhstar
@Supuhstar Thật tuyệt vời! 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


CẬP NHẬT: Cảm ơn tất cả mọi người upvotes tất cả mọi người, nhưng tôi không nghĩ rằng câu trả lời của tôi dưới đây là cách tốt nhất để viết enums trong Javascript nữa. Xem bài đăng trên blog của tôi để biết thêm chi tiết: Enums trong Javascript.


Cảnh báo tên là đã 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ể làm cho các đối tượng giá trị, vì vậy bạn có thể có bánh và ăn nó quá:

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ì nó là một ngôn ngữ động, thậm chí có thể thêm các giá trị enum vào tập hợp sau này:

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

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

// 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 nữa (giá trị đó thể hiện đúng thứ tự cho kích thước ví dụ).

Hãy nhớ rằng, trong Javascript một đối tượng giống như một bản đồ hoặc có thể bắt đầu. 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 xử lý chúng mà không biết nhiều về chúng trước.

VÍ 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à btw, nếu bạn quan tâm đến các không gian tên, bạn có thể muốn xem xét giải pháp của tôi về quản lý phụ thuộc không gian tên đơn giản nhưng mạnh mẽ cho javascript: Gói JS


477
2018-03-04 22:31



vậy thì làm thế nào bạn sẽ đi và tạo ra một SIZE đơn giản nếu bạn chỉ có tên của nó? - Johanisma
@ Johanisma: Đó là trường hợp sử dụng không thực sự có ý nghĩa cho enums như toàn bộ ý tưởng của họ 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ị phụ sau này trong Javascript. Tôi sẽ thêm một ví dụ về điều đó vào câu trả lời của tôi. - Stijn de Witt
Rất tốt trả lời. Tôi là một người dùng bình thường của JS / JQ, khi cần thiết và điều này đã giúp với một tình huống với SharePoint cần giải quyết. - GoldBishop
1 cho liên kết tới bài đăng của bạn bằng cách tiếp cận thuộc tính. Thanh lịch trong đó các khai báo cơ bản là đơn giản, như trong OP, với tính năng thêm thuộc tính khi muốn. - goodeye
@ DavideAndrea Bạn nói đúng. Tôi đã bắt đầu lưu trữ blog của mình dưới tên miền của riêng mình. Đã cập nhật liên kết. Cảm ơn bạn đã đề cập đến nó! - Stijn de Witt


Tóm lại: Bạn không thể.

Bạn có thể giả mạo nó, nhưng bạn sẽ không nhận được an toàn loại. 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ủa các giá trị chuỗi được ánh xạ tới các giá trị số nguyên. Ví dụ:

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

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

Vấn đề với cách tiếp cận này? Bạn có thể vô tình xác định lại số đếm của mình, hoặc vô tình có các giá trị đếm ngược trùng lặp. Ví dụ:

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

Chỉnh sửa 

Còn Object.freeze của Artur Czajka thì sao? Nó sẽ không hoạt động để ngăn bạn từ thứ hai đến thứ năm? - Fry Quad

Chắc chắn rồi, Object.freeze sẽ hoàn toàn khắc phục được sự cố mà tôi đã phàn nàn. Tôi muốn nhắc nhở mọi người rằng khi tôi viết ở trên, Object.freeze không thực sự tồn tại.

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

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

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

Mặc dù nó có thể không phù hợp với mọi sử dụng hợp lệ của enums, nhưng nó đi rất xa.


79
2017-08-21 20:56



có loại an toàn trong javascript? - Scott Evernden
Vì vậy, không ánh xạ giá trị cho 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 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
@Scott Evernden: điểm lấy. @ Khangax: điểm là nó vẫn là một hack. Enums chỉ đơn giản là không tồn tại trong Javascript, thời gian, kết thúc của câu chuyện. Ngay cả các mô hình được đề xuất bởi Tim Sylvester vẫn là một hack ít hơn lý tưởng. - Randolpho
Rải mã bằng các chữ cái không phải là rất duy trì, do đó nó có ý nghĩa để tạo các hằng số cho nó. Tất nhiên Javascript 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 thực thi, nhưng không nhiều trong Javascript có thể. Bạn có thể xác định lại hằng số, hoặc chức năng, hoặc chủ yếu là bất cứ điều gì. EG: document.getElementById = function () {alert ("Bạn đang say. Javascript không an toàn.");}; - Stijn de Witt
@ Randolpho: Còn Object.freeze của Artur Czajka thì sao? Nó sẽ không hoạt động để ngăn bạn từ thứ hai đến thứ năm? - Michael


Dưới đâ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 ra enums của bạn:

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

Bằng cách này, hằng số có thể được acessed 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 tiến nhỏ - bây giờ với varargs: (tiếc là nó không hoạt động đúng trên IE: S ... nên gắn liền 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');

46
2017-07-13 00:28





Trong hầu hết các trình duyệt hiện đại, có một ký hiệu kiểu dữ liệu nguyên thủy có thể được sử dụng để tạo một kiểu liệt kê. Nó sẽ đảm bảo an toàn loại của enum vì mỗi giá trị biểu tượng được đảm bảo bởi JavaScript là duy nhất, tức 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ả vào các giá trị enum:

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

Bản trình diễn Plunker

Trên GitHub bạn có thể tìm thấy một 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

41
2018-05-05 16:32



Đây là câu trả lời đúng về mặt lý thuyết. Trong thực tế, hỗ trợ trình duyệt năm 2015 không đủ. Không phải sản xuất đã sẵn sàng cho đến nay. - vbraun
Mặc dù hỗ trợ trình duyệt chưa có, đây là câu trả lời hay nhất vì điều này gần với những gì Symbol dành cho. - rvighne
Tuy nhiên, 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à deserialize. - Andy


Tôi đã chơi đùa với điều này, như tôi yêu enums của tôi. =)

Sử dụng Object.defineProperty Tô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 .. bạn nên (theo lý thuyết) có thể gọi và xác định 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:false điều nà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() trên đó. Các giá trị được chỉ định bắt đầu bằng 0 và gia tăng cho mỗi mục.

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

23
2017-08-21 10:34



Nếu bạn thêm return this; ở cuối Enum bạn có thể làm: var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW'); - HBP
Tôi không nghĩ vậy, 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 mucking lên không gian đối tượng (với chức năng toàn cầu ENUM). Chuyển đổi thành một hàm mkenum và thêm các phép gán số tùy chọn => var mixedUp = mkenum ('BLACK', {RED: 0x0F00, BLUE: 0X0F, XANH: 0x0F0, TRẮNG: 0x0FFF, ONE: 1}, HAI, BA, BỐN) ; // Thêm mã của tôi làm 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ày 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 quay trở lại ký hiệu đối tượng tiêu chuẩn. - Duncan
false là mặc định cho writable, enumerable và configurable. Không cần phải nhai qua mặc định. - ceving


Đây là một cái cũ mà tôi biết, nhưng cách mà nó đã được triển khai 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.Bar trả về 1 và MyEnum[1] trả về "Bar" bất kể thứ tự khai báo.


17
2018-06-24 16:11



Plus MyEnum ["Bar"] hoạt động mà trả về 1 ... <3 TypeScript cho đến nay ... - David Karlaš
và tất nhiên nếu bạn thực sự đang sử dụng Typecript: enum MyEnum { Foo, Bar, Foobar } - parliament


Đâ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 enums 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 enums 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ạ các enums 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 enums
  • Dễ dàng truy cập vào enums của bạn
  • Enums của bạn có thể là các 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 trong đó, như tôi giữ các tham chiếu đến các enums
  • Vẫn không có loại an toàn

15
2018-05-15 09:26





Nếu bạn đang sử dụng Xương sống, 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í sử dụ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()

12
2018-04-24 14:04





Trong ES7 , bạn có thể làm một ENUM thanh lịch dựa vào 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 --*/}

Lợi thế (của việc sử dụng lớp thay vì đối tượng theo nghĩa đen) là có một lớp cha Enumthì tất cả các Enums của bạn sẽ kéo dài lớp đó.

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

12
2018-01-06 07:07



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ư tôi đang thiếu một cái gì đó! - Jon G
Đừng làm thế. new ColorEnum() làm cho hoàn toàn không có ý nghĩa. - Bergi
mở rộng một enum âm thanh điên rồ, thực sự - Codii
một khi ngôn ngữ không hỗ trợ nó nguyên bản sẽ có ý nghĩa giữ quy ước này và sử dụng như thế này! tôi đồng ý! - Marcelo Filho