Dav*_*ron 1982 javascript syntax enums
在JavaScript中定义枚举的首选语法是什么?就像是:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
Run Code Online (Sandbox Code Playgroud)
还是有更优选的成语?
Art*_*jka 792
由于1.8.5可以密封和冻结对象,因此将上面定义为:
const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})
Run Code Online (Sandbox Code Playgroud)
要么
const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)
Run Code Online (Sandbox Code Playgroud)
瞧!JS枚举.
但是,这并不会阻止您为变量分配不需要的值,这通常是枚举的主要目标:
let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors
Run Code Online (Sandbox Code Playgroud)
确保更强的类型安全性(使用枚举或其他方式)的一种方法是使用TypeScript或Flow等工具.
不需要引用,但我保持它们的一致性.
Gar*_*eth 602
这不是一个很好的答案,但我会说,个人工作得很好
话虽如此,因为值是什么并不重要(你使用了0,1,2),我会使用一个有意义的字符串,以防你想输出当前值.
Sti*_*itt 495
更新:感谢所有人的支持,但我认为下面的答案不再是在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);
}
}
Run Code Online (Sandbox Code Playgroud)
或者,你可以制作值对象,这样你就可以吃蛋糕了:
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);
}
Run Code Online (Sandbox Code Playgroud)
在Javascript中,因为它是一种动态语言,甚至可以在以后为集合添加枚举值:
// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};
Run Code Online (Sandbox Code Playgroud)
请记住,身份检查不需要枚举的字段(本例中的值,名称和代码),只是为了方便起见.此外,size属性的名称本身不需要硬编码,但也可以动态设置.因此,假设您只知道新枚举值的名称,您仍然可以毫无问题地添加它:
// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};
Run Code Online (Sandbox Code Playgroud)
当然,这意味着不能再做出一些假设(例如,该值表示大小的正确顺序).
请记住,在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.
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果你对命名空间感兴趣,你可能想看看我的解决方案,为javascript简单但强大的命名空间和依赖管理:包JS
Ran*_*pho 82
底线:你不能.
你可以伪造它,但你不会得到类型安全.通常,这是通过创建映射到整数值的字符串值的简单字典来完成的.例如:
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Document.Write("Enumerant: " + DaysEnum.tuesday);
Run Code Online (Sandbox Code Playgroud)
这种方法有问题吗?您可能会意外地重新定义您的枚举,或意外地具有重复的枚举值.例如:
DaysEnum.monday = 4; // whoops, monday is now thursday, too
Run Code Online (Sandbox Code Playgroud)
编辑
Artur Czajka的Object.freeze怎么样?这不会阻止你设置星期一到星期四吗? - Fry Quad
当然,Object.freeze完全可以解决我抱怨的问题.我想提醒大家,当我写上面的内容时,Object.freeze并没有真正存在.
现在....现在它开辟了一些非常有趣的可能性.
编辑2
这是一个非常好的用于创建枚举的库.
http://www.2ality.com/2011/10/enums.html
虽然它可能不适合枚举的每一个有效用途,但它有很长的路要走.
And*_*Fi' 55
这就是我们所有人想要的:
function Enum(constantsList) {
for (var i in constantsList) {
this[constantsList[i]] = i;
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以创建您的枚举:
var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);
Run Code Online (Sandbox Code Playgroud)
通过这样做,常量可以通常的方式(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 */
};
Run Code Online (Sandbox Code Playgroud)
编辑 - 小改进 - 现在使用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');
Run Code Online (Sandbox Code Playgroud)
Vit*_*nko 52
在大多数现代浏览器中,有一个符号原始数据类型,可用于创建枚举.它将确保枚举的类型安全性,因为JavaScript保证每个符号值都是唯一的,即Symbol() != Symbol().例如:
const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});
Run Code Online (Sandbox Code Playgroud)
要简化调试,可以向枚举值添加说明:
const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});
Run Code Online (Sandbox Code Playgroud)
在GitHub上,您可以找到一个包装器,它简化了初始化枚举所需的代码:
const color = new Enum("RED", "BLUE")
color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE
Run Code Online (Sandbox Code Playgroud)
Dun*_*can 23
我一直在玩这个,因为我喜欢我的名词.=)
使用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
});
Run Code Online (Sandbox Code Playgroud)
由于该属性,writable:false这应该使其类型安全.
所以你应该能够创建一个自定义对象,然后调用Enum()它.分配的值从0开始,每个项目递增.
var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED; // == 0
EnumColors.BLUE; // == 1
EnumColors.GREEN; // == 2
EnumColors.YELLOW; // == 3
Run Code Online (Sandbox Code Playgroud)
Jac*_*fin 23
大多数人的"首选语法"已在上面列出.但是,存在一个主要的首要问题:性能.上述答案中没有一个在最轻微的情况下非常高效,它们都会使代码大小膨胀到极致.为了实现真正的性能,易于阅读代码,以及通过缩小来实现前所未有的代码大小减少,这是进行枚举的正确方法.
const ENUM_COLORENUM_RED = 0,
ENUM_COLORENUM_GREEN = 1,
ENUM_COLORENUM_BLUE = 2,
ENUMLEN_COLORENUM = 3;
// later on
if(currentColor === ENUM_COLORENUM_RED) {
// whatever
}
Run Code Online (Sandbox Code Playgroud)
此外,此语法允许清晰简洁的类扩展,如下所示.
(长度:2,450字节)
// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;
Run Code Online (Sandbox Code Playgroud)
有些人可能会说这比其他解决方案更不实用:它占用了大量空间,需要很长时间才能编写,并且没有涂上糖语法.是的,如果他们不缩小代码,那些人就是对的.但是,没有合理的人会在最终产品中留下未经授权的代码.对于这种缩小,Closure Compiler是我还没有找到的最好的.在线访问可以在这里找到.Closure编译器能够获取所有这些枚举数据并内联它,使你的javascript运行速度超快,并且超级小.观察.
(长度:605字节)
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]
});Run Code Online (Sandbox Code Playgroud)
现在,让我们看看没有任何这些枚举的等效文件有多大.
来源没有这些枚举(长度:1973个字节(477个字节短)!)
精缩没有这些枚举(长度:843个字节(238个字节长))
如图所示,没有枚举,源代码更短,代价是更大的缩小代码.我不知道你,我确信我不喜欢将源代码合并到最终产品中,这使得这种枚举方式更加优越,因为它会导致更小的文件大小.再加上这种形式的枚举更快.实际上,这种形式的枚举是要走的路.
Gov*_*Rai 18
TLDR:将此类添加到您的实用程序方法中并在整个代码中使用它,它会模仿传统编程语言中的Enum行为,并在您尝试访问不存在的枚举器或添加/更新枚举器时实际抛出错误.无需依赖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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后通过实例化类来创建枚举:
const roles = new Enum({
ADMIN: 'Admin',
USER: 'User',
});
Run Code Online (Sandbox Code Playgroud)
完整说明:
从传统语言中获得的Enums的一个非常有用的功能是,如果您尝试访问不存在的枚举器,它们会爆炸(抛出编译时错误).
除了冻结模拟的枚举结构以防止意外/恶意添加其他值之外,其他任何答案都没有解决Enums的内在特性.
您可能已经意识到,在JavaScript中访问不存在的成员只会返回undefined并且不会破坏您的代码.由于枚举数是预定义的常量(即一周中的几天),因此永远不应该存在未定义枚举数的情况.
不要误会我的意思,JavaScript undefined在访问未定义的属性时返回的行为实际上是语言的一个非常强大的功能,但是当你试图模仿传统的Enum结构时,它不是你想要的功能.
这是Proxy对象闪耀的地方.通过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 modifications
return 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 checks
console.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"Run Code Online (Sandbox Code Playgroud)
ASIDE:什么是代理?
我记得当我第一次开始在任何地方看到单词代理时,它对我来说绝对没有意义.如果你现在就是这样,我认为一种简单的方法来概括代理是将它们视为软件,机构,甚至是两个服务器,公司或人之间充当中间人或中间人的人.
Rob*_*rdy 17
这是我所知道的一个旧版本,但它通过TypeScript接口实现的方式是:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["Foo"] = 0] = "Foo";
MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));
Run Code Online (Sandbox Code Playgroud)
这使您可以查看MyEnum.Bar哪两个返回1,并MyEnum[1]返回"Bar"而不管声明的顺序如何.
Chr*_*ris 15
这是我使用的解决方案.
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;
}
Run Code Online (Sandbox Code Playgroud)
你定义你的枚举如下:
var COLORS = defineEnum({
RED : {
value : 1,
string : 'red'
},
GREEN : {
value : 2,
string : 'green'
},
BLUE : {
value : 3,
string : 'blue'
}
});
Run Code Online (Sandbox Code Playgroud)
这就是你访问你的枚举的方式:
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
});
Run Code Online (Sandbox Code Playgroud)
我通常使用最后两种方法来映射来自消息对象的枚举.
这种方法的一些优点:
一些缺点:
Abd*_*UMI 15
在ES7中,您可以依靠静态属性执行优雅的ENUM:
class ColorEnum {
static RED = 0 ;
static GREEN = 1;
static BLUE = 2;
}
Run Code Online (Sandbox Code Playgroud)
然后
if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}
Run Code Online (Sandbox Code Playgroud)
(使用类而不是文字对象)的优点是拥有一个父类,Enum然后所有的Enums都将扩展该类.
class ColorEnum extends Enum {/*....*/}
Run Code Online (Sandbox Code Playgroud)
hvd*_*vdd 14
创建一个对象文字:
const Modes = {
DRAGGING: 'drag',
SCALING: 'scale',
CLICKED: 'click'
};
Run Code Online (Sandbox Code Playgroud)
Yar*_*lav 11
如果您使用的骨干,你可以得到全面的枚举功能免费使用(通过ID,名称,自定义成员找到)Backbone.Collection.
// enum instance members, optional
var Color = Backbone.Model.extend({
print : function() {
console.log("I am " + this.get("name"))
}
});
// enum creation
var Colors = new Backbone.Collection([
{ id : 1, name : "Red", rgb : 0xFF0000},
{ id : 2, name : "Green" , rgb : 0x00FF00},
{ id : 3, name : "Blue" , rgb : 0x0000FF}
], {
model : Color
});
// Expose members through public fields.
Colors.each(function(color) {
Colors[color.get("name")] = color;
});
// using
Colors.Red.print()
Run Code Online (Sandbox Code Playgroud)
你的答案太复杂了
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
Run Code Online (Sandbox Code Playgroud)
我修改了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);
}
}
Run Code Online (Sandbox Code Playgroud)
测试:
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
Run Code Online (Sandbox Code Playgroud)
我对任何答案都不满意,所以我制作了Yet Another Enum (YEA!)。
这个实现:
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() + "]";
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
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; //0
colors.GREEN; //1
colors.BLUE; //2
colors.PORPLE; //3
colors[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
Run Code Online (Sandbox Code Playgroud)
IE8不支持freeze()方法.
来源:http://kangax.github.io/compat-table/es5/,点击"显示过时的浏览器?" 在顶部,并检查IE8和冻结行col交集.
在我目前的游戏项目中,我使用了以下内容,因为很少有客户仍在使用IE8:
var CONST_WILD_TYPES = {
REGULAR: 'REGULAR',
EXPANDING: 'EXPANDING',
STICKY: 'STICKY',
SHIFTING: 'SHIFTING'
};
Run Code Online (Sandbox Code Playgroud)
我们也可以这样做:
var CONST_WILD_TYPES = {
REGULAR: 'RE',
EXPANDING: 'EX',
STICKY: 'ST',
SHIFTING: 'SH'
};
Run Code Online (Sandbox Code Playgroud)
甚至这个:
var CONST_WILD_TYPES = {
REGULAR: '1',
EXPANDING: '2',
STICKY: '3',
SHIFTING: '4'
};
Run Code Online (Sandbox Code Playgroud)
最后一个,对于字符串来说似乎最有效,如果您有服务器和客户端交换这些数据,它会减少您的总带宽.
当然,现在你有责任确保数据中没有冲突(RE,EX等必须是唯一的,1,2等也应该是唯一的).请注意,为了向后兼容,您需要永久维护这些内容.
分配:
var wildType = CONST_WILD_TYPES.REGULAR;
Run Code Online (Sandbox Code Playgroud)
比较:
if (wildType === CONST_WILD_TYPES.REGULAR) {
// do something here
}
Run Code Online (Sandbox Code Playgroud)
我想出了这种方法,该方法以Java枚举为模型。这些是类型安全的,因此您也可以执行instanceof检查。
您可以这样定义枚举:
var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);
Run Code Online (Sandbox Code Playgroud)
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"
Run Code Online (Sandbox Code Playgroud)
实现:
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
}
})();
Run Code Online (Sandbox Code Playgroud)
var ColorEnum = {
red: {},
green: {},
blue: {}
}
Run Code Online (Sandbox Code Playgroud)
您不需要确保不会以这种方式将重复的数字分配给不同的枚举值。一个新对象被实例化并分配给所有枚举值。
最简单的解决方案:
var Status = Object.freeze({
"Connecting":0,
"Ready":1,
"Loading":2,
"Processing": 3
});
Run Code Online (Sandbox Code Playgroud)
console.log(Status.Ready) // 1
Run Code Online (Sandbox Code Playgroud)
console.log(Object.keys(Status)[Status.Ready]) // Ready
Run Code Online (Sandbox Code Playgroud)
es7方式,(迭代器,冻结),用法:
const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')
for (let name of ThreeWiseMen)
console.log(name)
// with a given key
let 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'
Run Code Online (Sandbox Code Playgroud)
代码:
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(', ') }
}
Run Code Online (Sandbox Code Playgroud)
这很有用:
const [CATS, DOGS, BIRDS] = ENUM();
Run Code Online (Sandbox Code Playgroud)
实现简单高效:
function * ENUM(count=1) { while(true) yield count++ }
Run Code Online (Sandbox Code Playgroud)
生成器可以生成所需的精确整数序列,而无需知道有多少常量。它还可以支持一个可选参数,该参数指定从哪个(可能是负数)数字开始(默认为1)。
| 归档时间: |
|
| 查看次数: |
946252 次 |
| 最近记录: |