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

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

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

还是有更好的成语?

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

话虽如此,因为它的值是什么都没有关系(你已经使用了0,1,2),所以我会用一个有意义的string来防止输出当前值。

更新 :感谢所有upvotes大家,但我不认为我的回答下面是用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中,因为它是一种dynamic语言,所以以后甚至可以将枚举值添加到集合中:

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

记住,枚举的字段(在这个例子中的值,名称和代码)不需要进行身份检查,只是为了方便。 size属性本身的名称也不需要硬编码,但也可以dynamic设置。 所以假设你只知道你的新枚举值的名字,你仍然可以添加它没有问题:

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

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

我不能发表评论的答案,所以我想我会碰到线程,因为它显示高谷歌。

从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枚举;)

来源: https : //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze

恕我直言,引号是不需要的,但我保持一致。

底线:你不能。

你可以伪造它,但你不会得到types安全。 通常这是通过创build映射到整数值的string值的简单字典来完成的。 例如:

 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呢? 这不会阻止你设置星期一到星期四吗? – 四人炒

当然, Object.freeze将完全解决我所抱怨的问题。 我想提醒大家,当我写上面的时候, Object.freeze并不存在。

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

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

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

虽然它可能不适合每一个有用的枚举,但是这个过程非常漫长。

这是我们都想要的:

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

现在你可以创build你的枚举了:

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

编辑 – 小改进 – 现在用可变参数:(不幸的是它不能正常工作在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'); 

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

使用Object.defineProperty我想我想出了一个可行的解决scheme。

这里是一个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应该使其types安全。

所以你应该可以创build一个自定义对象,然后调用Enum() 。 分配的值从0开始,每个项目增加一个值。

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

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

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

为了简化debugging,你可以添加一个描述枚举值:

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

Plunker演示

在GitHub上,你可以find一个简化初始化枚举所需的代码的包装:

 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 

这是我使用的解决scheme。

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

你可以像这样定义你的枚举:

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

这就是你如何访问你的枚举:

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

我通常使用最后2个方法来映射消息对象的枚举。

这种方法的一些优点:

  • 易于声明枚举
  • 轻松访问您的枚举
  • 你的枚举可以是复杂的types
  • 如果您使用getByValue很多,Enum类有一些关联caching

一些缺点:

  • 一些凌乱的内存pipe理正在进行,因为我保留对枚举的引用
  • 仍然没有types安全

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

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

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

如果您使用Backbone ,则可以使用Backbone.Collection免费获得全面的枚举function(通过id,名称,自定义成员查找)。

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

创build一个对象文字:

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

在ES7中 ,你可以依靠静态属性来做一个优雅的ENUM:

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

然后

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

(使用类而不是字面对象)的优点是有一个父类Enum那么所有的枚举将扩展该类。

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

我已经修改了Andre'Fi'的解决scheme:

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

testing:

 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 

你的答案太复杂了

 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 

IE8不支持freeze()方法。
资料来源: http : //kangax.github.io/compat-table/es5/ ,点击“显示过时的浏览器? 在顶部,并检查IE8&冻结行col交集。

在我目前的游戏项目中,我已经在下面使用了,因为很less有客户仍然使用IE8:

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

我们也可以这样做:

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

甚至这个:

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

最后一个看来对于string来说效率最高,如果你有服务器和客户端交换这个数据,它会减less你的总带宽。
当然,现在有责任确保数据中没有冲突(RE,EX等必须是唯一的,1,2也应该是唯一的)。 请注意,为了向后兼容,您需要永久维护这些内容。

分配:

 var wildType = CONST_WILD_TYPES.REGULAR; 

比较:

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

我做了一个枚举类,可以获取值和名称在O(1)。 它也可以生成一个包含所有名称和值的对象数组。

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

你可以这样启动它:

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

获取值(如C#中的枚举):

 var val2 = enum1.item2; 

为一个值取一个名字(当为不同的名字放入相同的值时可能会模棱两可):

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

要在对象中获取每个名称和值的数组:

 var arr = enum1.GetObjArr(); 

会产生:

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

您也可以随时获得html选项:

 var html = enum1.GetSelectOptionsHTML(); 

哪个持有:

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

一个简单快捷的方法是:

 var Colors = function(){ return { 'WHITE':0, 'BLACK':1, 'RED':2, 'GREEN':3 } }(); console.log(Colors.WHITE) //this prints out "0" 

我想出了在Java中使用枚举之后的这种方法。 这些是types安全的,所以你也可以执行instanceof检查。

你可以像这样定义枚举:

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

现在Days指的是Days枚举:

 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" 

执行:

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

即使在ES2015中也只支持静态方法 (而不是静态属性)(请参阅此处的第 15.2.2.2节),但您可以使用以下与Babel一起使用的es2015预设:

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

我发现,即使在不同的模块(例如,从另一个模块中导入CellState枚举),以及使用Webpack导入模块时,它也能按预期工作。

这个方法比其他大多数答案的好处是你可以和静态types检查器 (例如Flow ) 一起使用它 ,你可以在开发时使用静态types检查来声明你的variables,参数等是特定的CellState “枚举”而不是其他枚举(如果使用通用对象或符号,则不可能区分)。

更新

上面的代码有一个缺陷,就是它允许创buildCellStatetypes的其他对象(即使它不能被分配给CellState的静态字段,因为它被冻结了)。 尽pipe如此,下面更精细的代码提供了以下优点:

  1. 没有更多的CellStatetypes的CellState可能被创build
  2. 你保证没有两个枚举实例分配相同的代码
  3. 实用程序方法从string表示中获取枚举
  4. 返回枚举的所有实例的values函数不必以上述手动(并且容易出错)的方式创build返回值。

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

截至撰写时, 201410月 – 所以这里是一个当代的解决scheme。 我把这个解决scheme写成一个Node模块,并且包含了一个使用Mocha和Chai的testing,以及一个下划线JS。 你可以很容易地忽略这些,如果你喜欢的话只需要枚举代码。

看到很多post过于曲折的图书馆等。在Javascript中获得支持枚举的解决scheme是如此简单,它真的不需要。 这里是代码:

文件: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; 

And a test to illustrate what it gives you:

file: enumsSpec.js

 var chai = require("chai"), assert = chai.assert, expect = chai.expect, should = chai.should(), enums = require('./enums'), _ = require('underscore'); describe('enums', function () { describe('fileFormatEnum', function () { it('should return expected fileFormat enum declarations', function () { var fileFormatEnum = enums.fileFormatEnum; should.exist(fileFormatEnum); assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format'); assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format'); }); }); describe('encodingEnum', function () { it('should return expected encoding enum declarations', function () { var encodingEnum = enums.encodingEnum; should.exist(encodingEnum); assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format'); assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format'); }); }); }); 

As you can see, you get an Enum factory, you can get all the keys simply by calling enum.keys, and you can match the keys themselves to integer constants. And you can reuse the factory with different values, and export those generated Enums using Node's modular approach.

Once again, if you are just a casual user, or in the browser etc, just take the factory part of the code, potentially removing underscore library too if you don't wish to use it in your code.

Here's a couple different ways to implement TypeScript enums .

The easiest way is to just iterate over an object, adding inverted key-value pairs to the object. The only drawback is that you must manually set the value for each member.

 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 

And here's a lodash mixin to create an enum using a string. While this version is a little bit more involved, it does the numbering automatically for you. All the lodash methods used in this example have a regular JavaScript equivalent, so you can easily switch them out if you want.

 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 

I've just published an NPM package gen_enum allows you to create Enum data structure in Javascript quickly:

 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 

One nice thing about this little tool is in modern environment (including nodejs and IE 9+ browsers) the returned Enum object is immutable.

For more information please checkout https://github.com/greenlaw110/enumjs

更新

I obsolete gen_enum package and merge the function into constjs package, which provides more features including immutable objects, JSON string deserialization, string constants and bitmap generation etc. Checkout https://www.npmjs.com/package/constjs for more information

To upgrade from gen_enum to constjs just change the statement

 var genEnum = require('gen_enum'); 

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

你可以做这样的事情

 function Enum(){ this.add.apply(this,arguments); } Enum.prototype.add = function(){ for (var i in arguments) { this[arguments[i]] = new String(arguments[i]); } }; Enum.prototype.toList = function(){ return Object.keys(this) }; var STATUS = new Enum("CLOSED","PENDING"); var STATE = new Enum("CLOSED","PENDING"); STATE.CLOSED === STATUS.CLOSED // false; STATE.CLOSED === "CLOSED" // false; STATE.CLOSED.toString() === "CLOSED" // true; 

As defined in this library. https://github.com/webmodule/foo/blob/master/foo.js#L217

I had done it a while ago using a mixture of __defineGetter__ and __defineSetter__ or defineProperty depending on the JS version.

Here's the enum generating function I made: https://gist.github.com/gfarrell/6716853

You'd use it like this:

 var Colours = Enum('RED', 'GREEN', 'BLUE'); 

And it would create an immutable string:int dictionary (an enum).

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

You don't need to make sure you don't assign duplicate numbers to different enum values this way. A new object gets instantiated and assigned to all enum values.

It's easy to use, I think. https://stackoverflow.com/a/32245370/4365315

 var A = {a:11, b:22}, enumA = new TypeHelper(A); if(enumA.Value === Ab || enumA.Key === "a"){ ... } var keys = enumA.getAsList();//[object, object] //set enumA.setType(22, false);//setType(val, isKey) enumA.setType("a", true); enumA.setTypeByIndex(1); 

更新:

There is my helper codes( TypeHelper ).

 var Helper = { isEmpty: function (obj) { return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0; }, isObject: function (obj) { return (typeof obj === 'object'); }, sortObjectKeys: function (object) { return Object.keys(object) .sort(function (a, b) { c = a - b; return c }); }, containsItem: function (arr, item) { if (arr && Array.isArray(arr)) { return arr.indexOf(item) > -1; } else { return arr === item; } }, pushArray: function (arr1, arr2) { if (arr1 && arr2 && Array.isArray(arr1)) { arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]); } } }; function TypeHelper() { var _types = arguments[0], _defTypeIndex = 0, _currentType, _value, _allKeys = Helper.sortObjectKeys(_types); if (arguments.length == 2) { _defTypeIndex = arguments[1]; } Object.defineProperties(this, { Key: { get: function () { return _currentType; }, set: function (val) { _currentType.setType(val, true); }, enumerable: true }, Value: { get: function () { return _types[_currentType]; }, set: function (val) { _value.setType(val, false); }, enumerable: true } }); this.getAsList = function (keys) { var list = []; _allKeys.forEach(function (key, idx, array) { if (key && _types[key]) { if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) { var json = {}; json.Key = key; json.Value = _types[key]; Helper.pushArray(list, json); } } }); return list; }; this.setType = function (value, isKey) { if (!Helper.isEmpty(value)) { Object.keys(_types).forEach(function (key, idx, array) { if (Helper.isObject(value)) { if (value && value.Key == key) { _currentType = key; } } else if (isKey) { if (value && value.toString() == key.toString()) { _currentType = key; } } else if (value && value.toString() == _types[key]) { _currentType = key; } }); } else { this.setDefaultType(); } return isKey ? _types[_currentType] : _currentType; }; this.setTypeByIndex = function (index) { for (var i = 0; i < _allKeys.length; i++) { if (index === i) { _currentType = _allKeys[index]; break; } } }; this.setDefaultType = function () { this.setTypeByIndex(_defTypeIndex); }; this.setDefaultType(); } var TypeA = { "-1": "Any", "2": "2L", "100": "100L", "200": "200L", "1000": "1000L" }; var enumA = new TypeHelper(TypeA, 4); document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>"); enumA.setType("200L", false); document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>"); enumA.setDefaultType(); document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>"); enumA.setTypeByIndex(1); document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>"); document.writeln("is equals = ", (enumA.Value == TypeA["2"])); 

I wrote enumerationjs a very tiny library to address the issue which ensures type safety , allow enum constants to inherit from a prototype , guaranties enum constants and enum types to be immutable + many little features. It allows to refactor a lot of code and move some logic inside the enum definition. Here is an example :

 var CloseEventCodes = new Enumeration("closeEventCodes", { CLOSE_NORMAL: { _id: 1000, info: "Connection closed normally" }, CLOSE_GOING_AWAY: { _id: 1001, info: "Connection closed going away" }, CLOSE_PROTOCOL_ERROR: { _id: 1002, info: "Connection closed due to protocol error" }, CLOSE_UNSUPPORTED: { _id: 1003, info: "Connection closed due to unsupported operation" }, CLOSE_NO_STATUS: { _id: 1005, info: "Connection closed with no status" }, CLOSE_ABNORMAL: { _id: 1006, info: "Connection closed abnormally" }, CLOSE_TOO_LARGE: { _id: 1009, info: "Connection closed due to too large packet" } },{ talk: function(){ console.log(this.info); } }); CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet" CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true 

Enumeration is basically a factory.

Fully documented guide available here. 希望这可以帮助。

Simplest solution:

创build

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

Get Value

 console.log(Status.Ready) // 1 

Get Key

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

Really like what @Duncan did above, but I don't like mucking up global Object function space with Enum, so I wrote the following:

 function mkenum_1() { var o = new Object(); var c = -1; var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })}; for (i in arguments) { var e = arguments[i]; if ((!!e) & (e.constructor == Object)) for (j in e) f(j, (c=e[j])); else f(e, ++c); } return Object.freeze ? Object.freeze(o) : o; } var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE'); console.log("MED := " + Sizes.MEDIUM); console.log("LRG := " + Sizes.LARGE); // Output is: // MED := 1 // LRG := 100 

@Stijin also has a neat solution (referring to his blog) which includes properties on these objects. I wrote some code for that, too, which I'm including next.

 function mkenum_2(seed) { var p = {}; console.log("Seed := " + seed); for (k in seed) { var v = seed[k]; if (v instanceof Array) p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] }; else p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) }; } seed.properties = p; return Object.freeze ? Object.freeze(seed) : seed; } 

This version produces an additional property list allowing friendly name conversion and short codes. I like this version because one need not duplicate data entry in properties as the code does it for you.

 var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3}); var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] }); 

These two can be combined into a single processing unit, mkenum, (consume enums, assign values, create and add property list). However, as I've spent far too much time on this today already, I will leave the combination as an exercise for the dear reader.

What is an enum in my opinion: It's an immutable object that is always accessible and you can compare items with eachother, but the items have common properties/methods, but the objects themselves or the values cannot be changed and they are instantiated only once.

Enums are imho used for comparing datatypes, settings, actions to take/reply things like that.

So for this you need objects with the same instance so you can check if it is a enum type if(something instanceof enum) Also if you get an enum object you want to be able to do stuff with it, regardless of the enum type, it should always respond in the same way.

In my case its comparing values of datatypes, but it could be anything, from modifying blocks in facing direction in a 3d game to passing values on to a specific object type registry.

Keeping in mind it is javascript and doesn't provide fixed enum type, you end up always making your own implementation and as this thread shows there are legions of implementations without one being the absoulte correct.


This is what I use for Enums. Since enums are immutable(or should be at least heh) I freeze the objects so they can't be manipulated easely.

The enums can be used by EnumField.STRING and they have their own methods that will work with their types. To test if something passed to an object you can use if(somevar instanceof EnumFieldSegment)

It may not be the most elegant solution and i'm open for improvements, but this type of immutable enum(unless you unfreeze it) is exactly the usecase I needed.

I also realise I could have overridden the prototype with a {} but my mind works better with this format 😉 shoot me.

 /** * simple parameter object instantiator * @param name * @param value * @returns */ function p(name,value) { this.name = name; this.value = value; return Object.freeze(this); } /** * EnumFieldSegmentBase */ function EnumFieldSegmentBase() { this.fieldType = "STRING"; } function dummyregex() { } dummyregex.prototype.test = function(str) { if(this.fieldType === "STRING") { maxlength = arguments[1]; return str.length <= maxlength; } return true; }; dummyregexposer = new dummyregex(); EnumFieldSegmentBase.prototype.getInputRegex = function() { switch(this.fieldType) { case "STRING" : return dummyregexposer; case "INT": return /^(\d+)?$/; case "DECIMAL2": return /^\d+(\.\d{1,2}|\d+|\.)?$/; case "DECIMAL8": return /^\d+(\.\d{1,8}|\d+|\.)?$/; // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its false, otherwise lets see what Boolean produces case "BOOLEAN": return dummyregexposer; } }; EnumFieldSegmentBase.prototype.convertToType = function($input) { var val = $input; switch(this.fieldType) { case "STRING" : val = $input;break; case "INT": val==""? val=0 :val = parseInt($input);break; case "DECIMAL2": if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break; case "DECIMAL8": if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break; // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its false, otherwise lets see what Boolean produces case "BOOLEAN": val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf())) ;break; } return val; }; EnumFieldSegmentBase.prototype.convertToString = function($input) { var val = $input; switch(this.fieldType) { case "STRING": val = $input;break; case "INT": val = $input+"";break; case "DECIMAL2": val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break; case "DECIMAL8": val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break; case "BOOLEAN": val = $input ? "true" : "false" ;break; } return val; }; EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) { var val = false; switch(this.fieldType) { case "STRING": val = ($val1===$val2);break; case "INT": val = ($val1===$val2);break; case "DECIMAL2": val = ($val1.comparedTo($val2)===0);break; case "DECIMAL8": val = ($val1.comparedTo($val2)===0);break; case "BOOLEAN": val = ($val1===$val2);break; } return val; }; /** * EnumFieldSegment is an individual segment in the * EnumField * @param $array An array consisting of object p */ function EnumFieldSegment() { for(c=0;c<arguments.length;c++) { if(arguments[c] instanceof p) { this[arguments[c].name] = arguments[c].value; } } return Object.freeze(this); } EnumFieldSegment.prototype = new EnumFieldSegmentBase(); EnumFieldSegment.prototype.constructor = EnumFieldSegment; /** * Simple enum to show what type of variable a Field type is. * @param STRING * @param INT * @param DECIMAL2 * @param DECIMAL8 * @param BOOLEAN * */ EnumField = Object.freeze({STRING: new EnumFieldSegment(new p("fieldType","STRING")), INT: new EnumFieldSegment(new p("fieldType","INT")), DECIMAL2: new EnumFieldSegment(new p("fieldType","DECIMAL2")), DECIMAL8: new EnumFieldSegment(new p("fieldType","DECIMAL8")), BOOLEAN: new EnumFieldSegment(new p("fieldType","BOOLEAN"))});