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);}}
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.}}
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');
COLORS.BLUE.stringCOLORS.BLUE.valueCOLORS.getByName('BLUE').stringCOLORS.getByValue('value', 1).string
COLORS.forEach(function(e){// do what you want with e});
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
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}
})();
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)
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(', ') }
}
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',});
// 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"
/// 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工具结合在一起,使调试这种形式的枚举非常简单且非常容易。
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 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'
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