如何保证我的枚举定义不会在JavaScript中更改?

以下内容是否会使对象满足枚举在JavaScript中具有的所有特征?类似:

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

或者有其他方法我可以做到这一点吗?

1268699 次浏览

这算不上什么答案,但我得说,就我个人而言,这很有效

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

底线:你不能。

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

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

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

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

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

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

虽然它可能不适合枚举的所有有效用法,但它还有很长的路要走。

更新

我认为我下面的答案不再是用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 sizeSIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};

记住,枚举的字段(在这个例子中是value、name和code)并不需要进行身份检查,只是为了方便起见。另外size属性的name本身不需要硬编码,也可以动态设置。所以假设你只知道新枚举值的名称,你仍然可以毫无问题地添加它:

// Add 'Extra Large' size, only knowing it's namevar 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 szfor (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

从1.8.5开始,可以封存并冻结物体,所以将上面的定义为:

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

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

瞧!JS枚举。

但是,这并不能阻止您将不需要的值分配给变量,这通常是枚举的主要目标:

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

确保更强的类型安全性的一种方法(使用枚举或其他方式)是使用TypeScript流量之类的工具。

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

这是我们都想要的:

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

如果您使用的是骨干,您可以使用Backbone收藏免费获得完整的枚举功能(按id、名称、自定义成员查找)。

// enum instance members, optionalvar Color = Backbone.Model.extend({print : function() {console.log("I am " + this.get("name"))}});
// enum creationvar 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;});
// usingColors.Red.print()

这是我使用的解决方案。

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.stringCOLORS.BLUE.valueCOLORS.getByName('BLUE').stringCOLORS.getByValue('value', 1).string
COLORS.forEach(function(e){// do what you want with e});

我通常使用最后两个方法从消息对象映射枚举。

这种方法的一些优点:

  • 易于声明枚举
  • 轻松访问您的枚举
  • 枚举可以是复杂类型
  • 如果你经常使用getByValue,Enum类有一些关联缓存

一些缺点:

  • 那里有一些混乱的内存管理,因为我保留了对枚举的引用
  • 还是没有类型安全

我修改了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);}}

测试:

var Color = new Enum('RED', 'GREEN', 'BLUE');undefinedColor.name(Color.REDs)undefinedColor.name(Color.RED)"RED"Color.exist(Color.REDs)falseColor.exist(Color.RED)true

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

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

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

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

使用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;    // == 0EnumColors.BLUE;   // == 1EnumColors.GREEN;  // == 2EnumColors.YELLOW; // == 3

不久前,我使用了__defineGetter____defineSetter__defineProperty的混合物,具体取决于JS版本。

这是我创建的枚举生成函数:https://gist.github.com/gfarrell/6716853

你会像这样使用它:

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

它将创建一个不可变的字符串:int字典(枚举)。

一个快速而简单的方法是:

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

在撰写本文时,2014年10-所以这是一个当代的解决方案。我将解决方案编写为节点模块,并包含了使用Mocha和柴油的测试,以及下划线JS。你可以很容易地忽略这些,如果愿意,只需使用枚举代码。

看过很多带有过于复杂的库等的帖子。在Javascript中获得枚举支持的解决方案非常简单,真的不需要。这是代码:

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

还有一个测试来说明它给你带来了什么:

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

正如你所看到的,你得到了一个枚举工厂,你可以通过调用enum.keys来获得所有的键,你可以将键本身与整数常量匹配。你可以用不同的值重用工厂,并使用Node的模块化方法导出那些生成的枚举。

再一次,如果你只是一个普通用户,或者在浏览器等中,只需使用代码的工厂部分,如果你不想在代码中使用它,也可能删除下划线库。

你的答案太复杂了

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

真的很喜欢@Duncan上面所做的,但我不喜欢用Enum破坏全局对象函数空间,所以我写了以下内容:

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]));elsef(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也有一个简洁的解决方案(指他的博客),其中包括这些对象的属性。我也为此编写了一些代码,我接下来会包含这些代码。

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] };elsep[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };}seed.properties = p;
return Object.freeze ? Object.freeze(seed) : seed;}

这个版本产生了一个额外的属性列表,允许友好的名称转换和短代码。我喜欢这个版本,因为不需要像代码为你做的那样在属性中重复数据输入。

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

这两个可以组合成一个处理单元mkenum(使用枚举、赋值、创建和添加属性列表)。然而,由于我今天已经花了太多时间,我将把组合留给亲爱的读者作为练习。

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

您不需要确保不会以这种方式将重复的数字分配给不同的枚举值。一个新对象被实例化并分配给所有枚举值。

您可以使用Object.prototype.hasOwnProperty()

var findInEnum,colorEnum = {red : 0,green : 1,blue : 2};
// later on
findInEnum = function (enumKey) {if (colorEnum.hasOwnProperty(enumKey)) {return enumKey+' Value: ' + colorEnum[enumKey]}}
alert(findInEnum("blue"))

这里有几种不同的方法来实现TypeScript枚举

最简单的方法是遍历对象,向对象添加倒排键值对。唯一的缺点是您必须手动设置每个成员的值。

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


这里是一个黄豆拌粉,用于使用字符串创建枚举。虽然这个版本更复杂一些,但它会自动为您编号。这个例子中使用的所有洛达什方法都有一个常规的JavaScript等价物,所以如果你愿意,你可以很容易地切换它们。

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

意见中的枚举是什么:它是一个始终可访问的不可变对象,您可以相互比较项目,但是项目具有公共属性/方法,但是对象本身或值不能更改,并且它们只能实例化一次。

枚举是用来比较数据类型、设置、采取/回复类似的操作的。

因此,您需要具有相同实例的对象,以便检查它是否是枚举类型if(something instanceof enum)此外,如果你得到一个枚举对象,你希望能够用它做一些事情,不管枚举类型如何,它都应该以同样的方式响应。

在我的例子中,它比较数据类型的值,但它可以是任何东西,从修改3d游戏中面向方向的块到将值传递到特定的对象类型注册表。

记住它是javascript并且不提供固定的枚举类型,您最终总是制作自己的实现,正如这个线程所示,有大量的实现,而没有一个是绝对正确的。


这是我用于枚举的。由于枚举是不可变的(或者至少应该是heh),我冻结了对象,因此无法轻松操作它们。

枚举可以被EnumField.STRING使用,它们有自己的方法来处理它们的类型。要测试某些东西是否传递给对象,您可以使用if(somevar instanceof EnumFieldSegment)

它可能不是最优雅的解决方案,我愿意改进,但这种类型的不可变枚举(除非您解冻它)正是我需要的用例。

我也意识到我本可以用{}覆盖原型,但我的头脑更适合这种格式;-)开枪打我。

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

我刚刚发布了一个NPM包gen_enum,允许您快速在Javascript中创建Enum数据结构:

var genEnum = require('gen_enum');
var AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD');var curMode = AppMode.LOG_IN;console.log(curMode.isLogIn()); // output trueconsole.log(curMode.isSignUp()); // output falseconsole.log(curMode.isForgotPassword()); // output false

这个小工具的一个好处是在现代环境(包括nodejs和IE 9+浏览器)中,返回的Enum对象是不可变的。

欲了解更多信息,请查看https://github.com/greenlaw110/enumjs

更新

我废弃了gen_enum包并将函数合并到conjs包中,它提供了更多功能,包括不可变对象、JSON字符串反序列化、字符串常量和位图生成等。Checkouthttps://www.npmjs.com/package/constjs了解更多信息

要从gen_enum升级到constjs,只需更改语句

var genEnum = require('gen_enum');

var genEnum = require('constjs').enum;
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });

您不需要指定id,只需使用一个空对象来比较枚举。

if (incommingEnum === DaysEnum.monday) //incommingEnum is monday

编辑:如果您要序列化对象(例如JSON),您将再次使用id

在大多数现代浏览器中,有符号原始数据类型可用于创建枚举。它将确保枚举的类型安全,因为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) // REDcolor.size // 2color.values() // Symbol(RED), Symbol(BLUE)color.toString() // RED,BLUE

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

在我目前的游戏项目中,我使用了以下内容,因为很少有客户仍然使用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'};

最后一个,似乎对字符串最有效,如果您有服务器和客户端交换此数据,它会减少您的总带宽。
当然,现在你有责任确保数据中没有冲突(RE、EX等必须是唯一的,1、2等也应该是唯一的)。请注意,你需要永远维护这些数据以实现向后兼容。

任务:

var wildType = CONST_WILD_TYPES.REGULAR;

比较:

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

创建一个对象文字:

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

我认为它很容易使用。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]
//setenumA.setType(22, false);//setType(val, isKey)
enumA.setType("a", true);
enumA.setTypeByIndex(1);

更新:

有我的助手代码(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"]));

我想出了这个方法,它以Java中的枚举为模型。这些是类型安全的,因此您也可以执行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; // trueDays.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 // trueDays.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 enumvar __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}
})();

我写了enumerationjs一个非常小的库来解决这个问题,其中确保类型安全,允许枚举常量为从原型继承,保证枚举常量和枚举类型是不可变的+许多小功能。它允许重构大量代码并在枚举定义中移动一些逻辑。这是一个例子:

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基本上是一个工厂。

完整的文档指南可在此处获得。希望有帮助。

我创建了一个Enum类,它可以获取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 Enum = Object.freeze({Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })});
alert(Enum.Role.Supervisor);alert(Enum.Color.GREEN);var currentColor=0;if(currentColor == Enum.Color.RED) {alert('Its Red');}

你可以做这样的事

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

在这个库中定义。https://github.com/webmodule/foo/blob/master/foo.js#L217

完整示例https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026

最简单的解决方案:

创建

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

获取价值

console.log(Status.Ready) // 1

获取密钥

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

尽管ES2015支持只有静态方法(而不是静态属性)(参见这里,§15.2.2.2),但奇怪的是,您可以将以下内容与es2015预设的Babel一起使用:

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导入模块时都能按预期工作。

与大多数其他答案相比,此方法的优点是您可以将其与静态类型检查器一起使用(例如流量),您可以在开发时使用静态类型检查断言您的变量、参数等是特定的CellState“枚举”,而不是其他枚举(如果您使用通用对象或符号,则无法区分)。

更新

上面的代码有一个缺陷,它允许创建额外的CellState类型的对象(即使不能将它们分配给CellState的静态字段,因为它被冻结了)。尽管如此,下面更精致的代码提供了以下优点:

  1. 不能再创建类型CellState的对象
  2. 保证没有两个枚举实例被分配相同的代码
  3. 从字符串表示中获取枚举的实用方法
  4. 返回枚举的所有实例的values函数不必以上述手动(且容易出错)的方式创建返回值。

    '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}`);elsereturn Status.INSTANCES.get(code);}}
    Status.canCreateMoreInstances = false;Object.freeze(Status);exports.Status = Status;

外星人的解决方案是让事情尽可能简单:

  1. 使用enum关键字(在javascript中保留)
  2. 如果enum关键字只是保留但未在JavaScript中实现,请定义以下内容

    const enumerate = spec => spec.split(/\s*,\s*/).reduce((e, n) => Object.assign(e,{[n]:n}), {})

Now, you can easily use it

const kwords = enumerate("begin,end, procedure,if")console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)

我认为没有理由使枚举值显式变量。无论如何,脚本都是形态的,如果您的代码的一部分是字符串或有效代码,这没有区别。真正重要的是,无论何时使用或定义它们,您都不需要处理大量的引号。

ES7中,您可以根据静态属性执行优雅的ENUM:

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

然后

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

优点(使用类而不是文字对象)是有一个父类Enum,那么您的所有Enums都将延伸该类。

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

您可以尝试使用https://bitbucket.org/snippets/frostbane/aAjxM

my.namespace.ColorEnum = new Enum("RED = 0","GREEN","BLUE")

它应该工作到ie8。

您可以使用一个简单的函数来反转键和值,它也可以使用数组,因为它将数字整数字符串转换为数字。该代码很小,很简单,可重用于此和其他用例。

var objInvert = function (obj) {var invert = {}for (var i in obj) {if (i.match(/^\d+$/)) i = parseInt(i,10)invert[obj[i]] = i}return invert} 
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL','BOSSA-NOVA','POP','INDIE']))
console.log(musicStyles)

es7 way,(迭代器,冻结),用法:

const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')
for (let name of ThreeWiseMen)console.log(name)

// with a given keylet 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'

代码:

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

使用Javascript代理

TLDR:将此类添加到您的实用程序方法并在整个代码中使用它,它模拟传统编程语言中的枚举行为,并在您尝试访问不存在的枚举器或添加/更新枚举器时实际抛出错误。无需依赖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);}}

然后通过实例化类创建枚举:

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

完整解释:

您从传统语言中获得的Enums的一个非常有益的特性是,如果您尝试访问不存在的枚举器,它们会爆炸(引发编译时错误)。

除了冻结模拟枚举结构以防止意外/恶意添加其他值之外,其他答案都没有解决Enums的内在特征。

您可能知道,在JavaScript中访问不存在的成员只会返回undefined并且不会破坏您的代码。由于枚举器是预定义的常量(即一周中的几天),因此永远不应该出现枚举器应未定义的情况。

不要误会我的意思,JavaScript在访问未定义属性时返回undefined的行为实际上是语言的一个非常强大的功能,但当你试图模拟传统的Enum结构时,它不是你想要的功能。

这就是代理对象闪耀的地方。随着ES6(ES2015)的引入,代理在语言中被标准化。这是MDN的描述:

Proxy对象用于定义基本操作的自定义行为(例如属性查找、赋值、枚举、函数调用等)。

与Web服务器代理类似,JavaScript代理能够拦截对象上的操作(使用“陷阱”,如果您愿意,可以将其称为钩子),并允许您在完成之前执行各种检查,操作和/或操作(或者在某些情况下完全停止操作,这正是我们想要做的,如果我们尝试引用不存在的枚举器)。

这是一个使用Proxy对象来模拟Enums的人为示例。此示例中的枚举器是标准HTTP方法(即“GET”、“POST”等):

// 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 modificationsreturn 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 checksconsole.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:代理是什么鬼东西?

我记得当我第一次开始到处看到代理这个词时,很长一段时间对我来说绝对没有意义。如果现在是你,我认为概括代理的一个简单方法是将它们视为软件、机构,甚至是充当两个服务器、公司或人之间的中介或中间人的人。

这就是TypeScript将enum转换为Javascript的方式:

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

现在:

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

起初我很困惑为什么obj[1]返回'Active',但后来意识到它非常简单-赋值运算符分配值然后返回它:

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

𝗦𝗲𝗹𝗳-𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝘃𝗲 𝗘𝘅𝘁𝗲𝗻𝘀𝗶𝗯𝗹𝗲 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲 𝗡𝗮𝗺𝗲𝘀

让我们直奔问题:文件大小。这里列出的其他每个答案都会将你的缩小代码膨胀到极致。我向你展示的是,为了通过缩小、性能、代码易读性、大规模项目管理和许多代码编辑器中的语法提示来最大限度地减少代码大小,这是进行枚举的正确方法:下划线表示变量。


下划线符号变量

如上图和下面的示例所示,这里有五个简单的步骤可以开始:

  1. 确定枚举组的名称。考虑一个可以描述枚举目的或至少可以描述枚举中条目的的名词。例如,表示用户可选择的颜色的一组枚举可能比颜色更好地命名为颜色选择。
  2. 确定组中的枚举是互斥的还是独立的。如果互斥,则以ENUM_开头每个枚举的变量名。如果独立或并排,则使用INDEX_
  3. 对于每个条目,创建一个名称以ENUM_INDEX_开头的新局部变量,然后是组的名称,然后是下划线,然后是属性的唯一友好名称
  4. 在最后添加ENUMLENGTH_ENUMLEN_INDEXLENGTH_INDEXLEN_(无论是LEN_还是LENGTH_是个人偏好)的枚举变量。您应该在代码中尽可能使用此变量,以确保向枚举添加额外条目并递增此值不会破坏您的代码。
  5. 给每个连续的枚举变量一个比最后一个多的值,从0开始。这个页面上有评论说0不应该用作枚举值,因为0 == null0 == false0 == ""和其他JS疯狂。我向你提出,为了避免这个问题并同时提升性能,总是使用===,除了typeof(e. x.typeof X == "string")之外,永远不要让==出现在你的代码中。在我使用===的这些年里,我从来没有遇到过使用0作为枚举值的问题。如果您仍然感到紧张,那么1可以用作0 == null0枚举中的起始值(但不是0 == null1枚举中的起始值),在许多情况下不会造成性能损失。
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}

以下是我如何记住何时使用INDEX_和何时使用ENUM_

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

但是,在某些情况下,ENUM_可以作为索引,例如在计算每个项目的出现次数时。

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

请注意,在上面的代码中,添加一种新的宠物非常容易:您只需在ENUM_PET_RAT之后附加一个新条目并相应地更新ENUMLEN_PET。在其他枚举系统中添加新条目可能更困难和错误。


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

此外,这种枚举语法允许清晰简洁的类扩展,如下所示。要扩展一个类,在父类的LEN_条目中添加一个递增的数字。然后,用自己的LEN_条目结束子类,以便将来可以进一步扩展该子类。

附加扩展图

(function(window){"use strict";var parseInt = window.parseInt;
// use INDEX_ when representing the index in an array instanceconst INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPEINDEXLEN_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 typeENUM_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 greenconsole.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]);// the alpha of transparent purpleconsole.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]);// the enumerated array for turquoiseconsole.log(parseHexColor("#40E0D0"));})(self);

(长度:2,450字节)

有些人可能会说这不如其他解决方案实用:它浪费了大量空间,编写需要很长时间,并且它没有用糖语法包裹。如果这些人不缩小他们的代码,他们是对的。然而,没有一个合理的人会在最终产品中留下未缩小的代码。对于这种缩小,闭包编译器是我迄今为止找到的最好的。在线访问可以找到这里。闭包编译器能够获取所有这些枚举数据并内联它,使你的Javascript变得超级小而运行超级快。因此,使用闭包编译器缩小。观察。


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

闭包编译器能够通过推理执行一些非常令人难以置信的优化,这些推理远远超出了任何其他JavaScript小型程序的能力。闭包编译器能够内联设置为固定值的原始变量。闭包编译器还能够根据这些内联值进行推理,并消除if语句和循环中未使用的块。

通过闭包编译器编写代码

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

(长度:605字节)

闭包编译器奖励你更聪明地编码和更好地组织你的代码,因为,虽然许多小型器用更大的缩小文件大小来惩罚有组织的代码,但如果你使用变量名枚举等技巧,闭包编译器能够筛选你所有的清洁和理智,输出更小的文件大小。在这种情况下,这是编码的圣杯:一个既能帮助你的代码缩小尺寸,又能通过训练更好的编程习惯来帮助你的大脑的工具。


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

现在,让我们看看如果没有这些枚举,等效文件会有多大。

不使用枚举的源(长度:1,973字节(比枚举代码短477字节!))
缩小而不使用枚举(长度:843字节(238字节长于枚举代码))

代码大小图表



如图所示,没有枚举,源代码更短,代价是更大的缩小代码。我不知道你;但我确信我不会将源代码合并到最终产品中。因此,这种形式的枚举远远优于它导致更小的缩小文件大小。


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

这种枚举形式的另一个优点是它可以用于轻松管理大型项目,而不会牺牲最小的代码大小。当与许多其他人一起处理大型项目时,显式标记和标记变量名以及代码创建者可能是有益的,这样可以快速识别代码的原始创建者以进行协作bug修复。

// JG = Jack Giffinconst 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 Knightconst ENUM_PL_ARRAYTYPE_UNSORTED   = 0,ENUM_PL_ARRAYTYPE_ISSORTED   = 1,ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob KnightENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffinENUMLEN_PL_COLORENUM         = 4;
// later on
if(randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||randomArray === ENUM_BK_ARRAYTYPE_CHUNKED) {// whatever}

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

此外,这种形式的枚举在缩小后也快得多。在普通的命名属性中,浏览器必须使用哈希映射来查找属性在对象上的位置。尽管JIT编译器智能地缓存了对象上的这个位置,但由于特殊情况,如从对象中删除较低的属性,仍然会有巨大的开销。

但是,对于连续的非稀疏整数索引PACKED_ELEMENTS数组,浏览器能够跳过大部分开销,因为内部数组中值的索引已经指定。是的,根据ECMAScript标准,所有属性都应该被视为字符串。然而,ECMAScript标准的这一方面在性能方面非常误导,因为所有浏览器都对数组中的数字索引进行了特殊优化。

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

将上面的代码与下面的代码进行比较。

/// Arrays, however, are always lightning fastconst 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";

有人可能会反对枚举的代码看起来比普通对象的代码长得多,但外观可能具有欺骗性。在使用史诗级闭包编译器时,请务必记住源代码大小与输出大小不成比例。观察。

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

上面是不带枚举的缩小代码,下面是带枚举的缩小代码。

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

上面的示例表明,除了具有卓越的性能外,枚举代码还会导致更小的缩小文件大小。


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

此外,这个人的个人樱桃在上面是在Javascript模式下使用这种形式的枚举和代码镜像文本编辑器。CodeMirror的Javascript语法高亮模式突出显示当前范围内的局部变量。这样,当你正确输入变量名时,你会立即知道,因为如果变量名之前是用var关键字声明的,那么变量名会变成一种特殊的颜色(默认情况下是青色)。即使你不使用CodeMirror,那么至少浏览器在执行输入错误的枚举名称的代码时会抛出一个有用的[variable name] is not defined异常。此外,JSLint和CloURE Compiler等JavaScript工具在你输入错误的枚举变量名时会大声告诉你。CodeMirror、浏览器和各种Javascript工具结合在一起,使调试这种形式的枚举非常简单且非常容易。

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}

在上面的代码片段中,您收到了错误警报,因为ENUM_COLORENUM_DNE不存在。


𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻

我认为可以肯定地说,这种枚举方法论不仅是缩小代码大小的最佳方式,也是性能、调试和协作的最佳方式。

这个答案是针对特定情况的另一种方法。我需要一组基于属性子值的位掩码常量(属性值是数组或值列表的情况)。它包含多个重叠枚举的等价物。

我创建了一个类来存储和生成位掩码值。然后,我可以以这种方式使用伪常量位掩码值来测试,例如,RGB值中是否存在绿色:

if (value & Ez.G) {...}

在我的代码中,我只创建了这个类的一个实例。如果不实例化该类的至少一个实例,似乎没有一种干净的方法可以做到这一点。这是类声明和位掩码值生成代码:

class Ez {constructor() {let rgba = ["R", "G", "B", "A"];let rgbm = rgba.slice();rgbm.push("M");              // for feColorMatrix values attributethis.createValues(rgba);this.createValues(["H", "S", "L"]);this.createValues([rgba, rgbm]);this.createValues([attX, attY, attW, attH]);}createValues(a) {                // a for arraylet i, j;if (isA(a[0])) {             // max 2 dimensionslet k = 1;for (i of a[0]) {for (j of a[1]) {this[i + j] = k;k *= 2;}}}else {                       // 1D array is simple loopfor (i = 0, j = 1; i < a.length; i++, j *= 2)this[a[i]] = j;}}

2D数组用于SVG feColor Matrix值属性,它是RGBAM的RGBA的4x5矩阵,其中M是乘数。生成的Ez属性是Ez.RR、Ez.RG等。

class Enum {constructor (...vals) {vals.forEach( val => {const CONSTANT = Symbol(val);Object.defineProperty(this, val.toUpperCase(), {get () {return CONSTANT;},set (val) {const enum_val = "CONSTANT";// generate TypeError associated with attempting to change the value of a constantenum_val = val;}});});}}

使用示例:

const COLORS = new Enum("red", "blue", "green");

阅读所有答案,没有找到任何非冗长和干燥的解决方案。我使用这个单行代码:

const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});

它生成一个具有人类可读值的对象:

{DRAW: 'DRAW',SCALE: 'SCALE',DRAG: 'DRAG'}

我对任何一个答案都不满意,所以我做了另一个枚举(YEA!)

此实现:

  • 使用更多最新的JS
  • 只需要这个类的声明就可以轻松创建枚举
  • 具有按名称(colors.RED)、字符串(colors["RED"])和索引(colors[0])的映射,但您只需将字符串作为数组传入
  • 将等效的toString()valueOf()函数绑定到每个枚举对象(如果不希望这样做,可以简单地将其删除-JS的开销很小)
  • 具有可选的全局命名/按名称字符串存储
  • 一旦创建就冻结枚举对象,使其无法修改

特别感谢Andre'Fi的回答的一些灵感。


代码:

class Enums {static create({ name = undefined, items = [] }) {let newEnum = {};newEnum.length = items.length;newEnum.items = items;for (let itemIndex in items) {//Map by name.newEnum[items[itemIndex]] = parseInt(itemIndex, 10);//Map by index.newEnum[parseInt(itemIndex, 10)] = items[itemIndex];}newEnum.toString = Enums.enumToString.bind(newEnum);newEnum.valueOf = newEnum.toString;//Optional naming and global registration.if (name != undefined) {newEnum.name = name;Enums[name] = newEnum;}//Prevent modification of the enum object.Object.freeze(newEnum);return newEnum;}static enumToString() {return "Enum " +(this.name != undefined ? this.name + " " : "") +"[" + this.items.toString() + "]";}}

用法:

let colors = Enums.create({name: "COLORS",items: [ "RED", "GREEN", "BLUE", "PORPLE" ]});
//Global access, if named.Enums.COLORS;
colors.items; //Array(4) [ "RED", "GREEN", "BLUE", "PORPLE" ]colors.length; //4
colors.RED; //0colors.GREEN; //1colors.BLUE; //2colors.PORPLE; //3colors[0]; //"RED"colors[1]; //"GREEN"colors[2]; //"BLUE"colors[3]; //"PORPLE"
colors.toString(); //"Enum COLORS [RED,GREEN,BLUE,PORPLE]"
//Enum frozen, makes it a real enum.colors.RED = 9001;colors.RED; //0

这可能是有用的:

const [CATS, DOGS, BIRDS] = ENUM();

实现简单而高效:

function * ENUM(count=1) { while(true) yield count++ }

生成器可以生成所需的确切整数序列,而不知道有多少常量。它还可以支持一个可选参数,该参数指定从哪个(可能是负的)数字开始(默认为1)。

export const ButtonType = Object.freeze({DEFAULT: 'default',BIG: 'big',SMALL: 'small'})

来源:https://medium.com/@idanlevi2/enum-in-javascript 5f2ff500f149

更新05.11.2020:
修改为包含静态字段和方法以更接近复制“true”枚举行为。

有没有人尝试过对包含私有字段和“获取”访问器的类执行此操作?我意识到私有类字段在这一点上仍然是实验性的,但它似乎适用于创建一个具有不可变字段/属性的类。浏览器支持也不错。唯一不支持它的“主要”浏览器是Firefox(我相信他们很快就会支持)和IE(谁在乎)。

免责声明
我不是开发人员。我只是在寻找这个问题的答案,并开始思考我有时如何通过创建带有私有字段和受限属性访问器的类来在C#中创建“增强”枚举。

样本类

class Sizes {// Private Fieldsstatic #_SMALL = 0;static #_MEDIUM = 1;static #_LARGE = 2;
// Accessors for "get" functions only (no "set" functions)static get SMALL() { return this.#_SMALL; }static get MEDIUM() { return this.#_MEDIUM; }static get LARGE() { return this.#_LARGE; }}

您现在应该能够直接调用枚举。

Sizes.SMALL; // 0Sizes.MEDIUM; // 1Sizes.LARGE; // 2

使用私有字段和有限访问器的组合意味着枚举值得到了很好的保护。

Sizes.SMALL = 10 // Sizes.SMALL is still 0Sizes._SMALL = 10 // Sizes.SMALL is still 0Sizes.#_SMALL = 10 // Sizes.SMALL is still 0

这是我对(标记)Enum工厂的看法。这是工作演示

/** Notes:* The proxy handler enables case insensitive property queries* BigInt is used to enable bitflag strings /w length > 52*/function EnumFactory() {const proxyfy = {construct(target, args) {const caseInsensitiveHandler = {get(target, key) {return target[key.toUpperCase()] || target[key];}};const proxified = new Proxy(new target(...args), caseInsensitiveHandler );return Object.freeze(proxified);},}const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);const throwIf = (assertion = false,message = `Unspecified error`,ErrorType = Error ) =>assertion && (() => { throw new ErrorType(message); })();const hasFlag = (val, sub) => {throwIf(!val || !sub, "valueIn: missing parameters", RangeError);const andVal = (sub & val);return andVal !== BigInt(0) && andVal === val;};
function EnumCtor(values) {throwIf(values.constructor !== Array ||values.length < 2 ||values.filter( v => v.constructor !== String ).length > 0,`EnumFactory: expected Array of at least 2 strings`, TypeError);const base = BigInt(1);this.NONE = BigInt(0);values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );}
EnumCtor.prototype = {get keys() { return Object.keys(this).slice(1); },subset(sub) {const arrayValues = this.keys;return new ProxiedEnumCtor([...sub.toString(2)].reverse().reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] ));},getLabel(enumValue) {const tryLabel = Object.entries(this).find( value => value[1] === enumValue );return !enumValue || !tryLabel.length ?"getLabel: no value parameter or value not in enum" :tryLabel.shift();},hasFlag(val, sub = this) { return hasFlag(val, sub); },};  
return arr => new ProxiedEnumCtor(arr);}

我也在寻找这个问题的答案,发现这个页面的答案我认为与这里的大多数答案不同:https://www.sohamkamani.com/javascript/enums/

我会把文章的答案部分复制到这里,以防链接在未来或其他地方无效:

带有符号的枚举:

符号让我们定义保证不冲突的值#36825;的人

例如:

const Summer1 = Symbol("summer")const Summer2 = Symbol("summer")
// Even though they have the same apparent value// Summer1 and Summer2 don't equateconsole.log(Summer1 === Summer2)// false
console.log(Summer1)

我们可以使用符号来定义枚举,以确保它们不是重复:

const Summer = Symbol("summer")const Autumn = Symbol("autumn")const Winter = Symbol("winter")const Spring = Symbol("spring")
let season = Spring
switch (season) {case Summer:console.log('the season is summer')break;case Winter:console.log('the season is winter')break;case Spring:console.log('the season is spring')break;case Autumn:console.log('the season is autumn')break;default:console.log('season not defined')}

使用符号确保我们分配枚举值的唯一方法是使用我们最初定义的常量。

枚举类:

为了使我们的代码在语义上更加正确,我们可以创建一个类来保存枚举组。

例如,我们的季节应该有办法让我们识别它们都属于类似的分类。

让我们看看如何使用类和对象来创建不同的枚举组:

// Season enums can be grouped as static members of a classclass Season {// Create new instances of the same class as static attributesstatic Summer = new Season("summer")static Autumn = new Season("autumn")static Winter = new Season("winter")static Spring = new Season("spring")
constructor(name) {this.name = name}}
// Now we can access enums using namespaced assignments// this makes it semantically clear that "Summer" is a "Season"let season = Season.Summer
// We can verify whether a particular variable is a Season enumconsole.log(season instanceof Season)// trueconsole.log(Symbol('something') instanceof Season)//false
// We can explicitly check the type based on each enums classconsole.log(season.constructor.name)// 'Season'

个人注意:我会使用这个构造函数:(注意:将:this.name设置为字符串而不是对象,会丢失下面的一些验证。可选地删除:.description。我还想找到一种不必键入Seasons.summer.name但只需要:Seasons.summer即可使其返回字符串的方法)

  constructor(name) {this.name = Symbol(name).description}

列出所有可能的枚举值:

如果我们使用上面基于类的方法,我们可以循环遍历用于获取相同条件下所有枚举值的季节类的键组:

Object.keys(Season).forEach(season => console.log("season:", season))// season: Summer// season: Autumn// season: Winter// season: Spring

什么时候在JavaScript中使用枚举?

一般来说,如果有一定数量的固定值,枚举是有帮助的任何一个variable_

例如,Node.js的加密标准库有一个支持算法列表,可以认为是枚举组。

在Javascript中正确使用枚举将导致更好的代码更稳定,更易于阅读,更少出错。

您只需要使用Object.freeze(创建一个不可变对象:

export const ColorEnum = Object.freeze({// you can only change the property values here// in the object declaration like in the Java enumarationRED: 0,GREEN: 1,BLUE: 2,});
ColorEnum.RED = 22    // assigning here will throw an errorColorEnum.VIOLET = 45 // even adding a new property will throw an error