题 在JavaScript中定义枚举的首选语法是什么?


在JavaScript中定义枚举的首选语法是什么?就像是:

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

// later on

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

还是有更优选的成语?


1678
2017-11-13 19:09


起源


我在odesk上对Javascript 1.8进行了测试,他们询问了什么应该解决javascript中的枚举,2个明显的答案是let关键字或生成器。我依稀记得我的c ++课程中的枚举,真的不知道什么是发电机;但这可能有所帮助。 - Devin G Rhode
不要用 0 作为枚举数。除非它用于尚未设置的东西。 JS对待 false || undefined || null || 0 || "" || '' || NaN 使用时,所有值都相同 ==。 - matsko
@matsko不仅仅是反对使用==的参数吗? - sdm350
但 false == 0 和 +null == 0 (当你不指望它时,有时候会转换成数字) null == undefined 也是, +undefined 是 NaN (虽然 NaN != NaN)。 - sanderd17
双重等式矩阵比微软词的自动格式化更令人困惑 - aaaaaa


答案:


由于1.8.5可以密封和冻结对象,因此将上面定义为:

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

要么

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

瞧! JS枚举。

但是,这并不妨碍您为变量分配不需要的值,这通常是枚举的主要目标:

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

确保更强的类型安全性(使用枚举或其他方式)的一种方法是使用类似的工具 打字稿 要么

资源

不需要引用,但我保持它们的一致性。


541
2018-02-18 11:03



根据维基百科(en.wikipedia.org/wiki/JavaScript#Versions)它适用于Firefox 4,IE 9,Opera 11.60,我知道它适用于Chrome。 - Artur Czajka
这是2012年的正确答案。更简单: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });。您不需要指定id,只需使用空对象来比较枚举。 if (incommingEnum === DaysEnum.monday) //incommingEnum is monday - Gabriel Llamas
为了向后兼容, if (Object.freeze) { Object.freeze(DaysEnum); } - saluce
我想指出这样做 ({ monday: {},  等等意味着如果您通过stringify将该对象转换为JSON,您将获得 [{"day": {}}] 哪个不行。 - jcollum
@Supuhstar我现在对这个问题的看法是不同的。不要使用freeze(),它完全没用,浪费时间做“愚蠢”的事情。如果要公开枚举,只需公开: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}。比较之前评论中的对象比比较数字要快得多。 - Gabriel Llamas


这不是一个很好的答案,但我会说,个人工作得很好

话虽如此,因为值是什么并不重要(你使用了0,1,2),我会使用一个有意义的字符串,以防你想输出当前值。


583
2017-11-13 19:13



这是我创建的一个简单的枚举工厂; npmjs.org/package/simple-enum - Tolga E
抱歉,这个枚举实现有几个问题。我建议改用: github.com/rauschma/enums - paldepind
这是在另一个答案中说明的,但由于这个答案是公认的答案,我将在此发布。 OP的解决方案是正确的。但是,如果使用它会更好 Object.freeze()。这将阻止其他代码更改枚举值。例: var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2}); - Sildoreth
@TolgaE谢谢你的图书馆!它激励我不仅将其煮至最低限度,而且还添加了几个功能!我把你的分开了,把它放在这里: github.com/BlueHuskyStudios/Micro-JS-Enum - Supuhstar
@Supuhstar太棒了!我很高兴你可以使用它..如果你想将它合并到这个库中,请随意提出拉取请求,然后我可以更新npm库 - Tolga E


UPDATE:感谢所有人的支持,但我认为下面的答案不再是在Javascript中编写枚举的最佳方式。有关详细信息,请参阅我的博文: Javascript中的枚举


警告名称已经成为可能:

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

或者,你可以制作值对象,这样你就可以吃蛋糕了:

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

在Javascript中,因为它是一种动态语言,甚至可以在以后向集合中添加枚举值:

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

请记住,身份检查不需要枚举的字段(本例中的值,名称和代码),只是为了方便起见。此外,size属性的名称本身不需要硬编码,但也可以动态设置。因此,假设您只知道新枚举值的名称,您仍然可以毫无问题地添加它:

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

当然,这意味着不能再做出一些假设(例如,该值表示大小的正确顺序)。

请记住,在Javascript中,对象就像地图或哈希表。一组名称 - 值对。您可以在不事先了解它们的情况下循环遍历它们或以其他方式操纵它们。

例如:

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

顺便说一句,如果你对命名空间感兴趣,你可能想看看我的解决方案,为javascript简单而强大的命名空间和依赖关系管理: 包JS


477
2018-03-04 22:31



那么如果你只有它的名字,你将如何去创建一个SIZE? - Johanisma
@Johanisma:对于枚举来说,这个用例并不真正有意义,因为对它们的全部想法是你事先知道所有的价值观。但是,没有什么可以阻止您稍后在Javascript中添加额外的值。我将在答案中添加一个例子。 - Stijn de Witt
很好的答案。在需要时,我是JS / JQ的临时用户,这有助于解决需要解决的SharePoint问题。 - GoldBishop
+1使用属性方法链接到您的帖子。优雅的基本声明很简单,如在OP中,在需要时添加了属性功能。 - goodeye
@DavideAndrea你是对的。我已经开始在我自己的域名下托管我的博客了。更新了链接。谢谢你提到它! - Stijn de Witt


底线:你不能。

你可以假装它,但你不会得到类型安全。通常,这是通过创建映射到整数值的字符串值的简单字典来完成的。例如:

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

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

这种方法有问题吗?您可能会意外地重新定义您的枚举,或意外地具有重复的枚举值。例如:

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

编辑 

Artur Czajka的Object.freeze怎么样?这不会阻止你设置星期一到星期四吗? - Fry Quad

绝对, Object.freeze 完全可以解决我抱怨的问题。我想提醒大家,当我写上面的内容时, Object.freeze 并不存在。

现在....现在它打开了一些 非常 有趣的可能性

编辑2
这是一个非常好的用于创建枚举的库。

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

虽然它可能不适合枚举的每一个有效用途,但它有很长的路要走。


79
2017-08-21 20:56



javascript中有类型安全吗? - Scott Evernden
所以不要将值映射到对象属性。使用getter访问enumerant(存储为“private”对象的属性)。一个天真的实现看起来像 - var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1 - kangax
@Scott Evernden:点了。 @kangax:重点是它仍然是一个黑客。枚举根本不存在于Javascript,句号,故事结尾。即使是蒂姆西尔维斯特提出的模式仍然不是理想的黑客。 - Randolpho
使用文字来填充代码是不可维护的,因此为它创建常量是有意义的。当然Javascript也没有常量。所以基本上这只是编写干净代码的一种方法。它不能强制执行,但Javascript可以不多。您可以重新定义常量或函数,或大多数任何东西。 EG:document.getElementById = function(){alert(“你搞砸了.Javascript不是类型安全的。”);}; - Stijn de Witt
@Randolpho:Artur Czajka的Object.freeze怎么样?这不会阻止你设置星期一到星期四吗? - Michael


这就是我们所有人想要的:

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

现在您可以创建您的枚举:

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

通过这样做,常数可以通常的方式(YesNo.YES,Color.GREEN)获得,并且它们获得顺序的int值(NO = 0,YES = 1; RED = 0,GREEN = 1,BLUE = 2)。

您还可以使用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 */
};


编辑 - 小改进 - 现在使用varargs :(不幸的是它在IE上无法正常工作:S ...应该坚持以前的版本)

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





在大多数现代浏览器中,有一个 符号 原始数据类型,可用于创建枚举。它将确保枚举的类型安全性,因为JavaScript保证每个符号值是唯一的,即 Symbol() != Symbol()。例如:

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

要简化调试,可以向枚举值添加说明:

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

Plunker演示

GitHub上 你可以找到一个简化初始化枚举所需代码的包装器:

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



这是理论上的正确答案。在实践中,2015年的浏览器支持远远不够。到目前为止尚未准备好生产 - vbraun
虽然浏览器支持还没有,但这是最好的答案,因为它接近于什么 Symbol 是为了。 - rvighne
Meh ...枚举值通常需要是可序列化的,而且Symbols不是那么方便序列化和反序列化。 - Andy


我一直在玩这个,因为我喜欢我的爱情。 =)

运用 Object.defineProperty 我想我想出了一个可行的解决方案。

这是一个jsfiddle: http://jsfiddle.net/ZV4A6/

使用此方法..您应该(理论上)能够为任何对象调用和定义枚举值,而不会影响该对象的其他属性。

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

因为属性 writable:false 这个 应该 使它类型安全。

所以你应该能够创建一个自定义对象,然后调用 Enum() 在上面。分配的值从0开始,每个项目递增。

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



如果你添加 return this; 在Enum结束时你可以这样做: var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW'); - HBP
我没有考虑到这一点,因为这不是我正常的做事方法。但你绝对正确!我会编辑它。 - Duncan
我真的很喜欢这个,虽然我不喜欢破坏对象空间(具有全局函数ENUM)。将其转换为mkenum函数并添加了可选的数字赋值=> var mixedUp = mkenum('BLACK',{RED:0x0F00,BLUE:0X0F,GREEN:0x0F0,WHITE:0x0FFF,ONE:1},TWO,THREE,FOUR) ; //在下面添加我的代码作为答案。谢谢。 - Andrew Philips
说实话,我甚至不再使用它了。我一直在使用谷歌的Closure Compiler,如果使用高级设置,这不能很好地工作(或者只是让事情变得复杂)。所以我刚刚回到标准对象表示法。 - Duncan
false 是默认值 writable, enumerable 和 configurable。无需咀嚼违约。 - ceving


这是我所知道的旧版本,但是它通过TypeScript接口实现的方式是:

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

这使您可以查看两者 MyEnum.Bar 返回1,和 MyEnum[1] 无论声明的顺序如何,都返回“Bar”。


17
2018-06-24 16:11



加上MyEnum [“Bar”]可以返回1 ... <3 TypeScript到目前为止...... - David Karlaš
当然,如果你真的使用的是Typescript: enum MyEnum { Foo, Bar, Foobar } - parliament