以下是否会使对象满足枚举在 JavaScript 中的所有特征?就像是:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
还是有其他方法可以做到这一点?
0
作为枚举数。除非它用于尚未设置的东西。当使用 ==
进行比较时,JS 将 false || undefined || null || 0 || "" || '' || NaN
视为相同的值。
0 == null
返回错误
false == 0
和 +null == 0
(有时会在您意想不到的情况下转换为数字),而 null == undefined
也是如此,并且 +undefined
是 NaN
(尽管是 NaN != NaN
)。
从 1.8.5 开始可以使用 seal and freeze the object,因此将上述定义为:
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.tuesday
day = 298832342 // goes through without any errors
确保更高程度的类型安全(使用枚举或其他方式)的一种方法是使用像 TypeScript 或 Flow 这样的工具。
不需要引号,但我保留它们以保持一致性。
这不是一个很好的答案,但我个人认为这很好用
话虽如此,由于值是什么并不重要(您使用了 0、1、2),我会使用有意义的字符串以防您想要输出当前值。
Object.freeze()
一起使用,效果会更好。这将防止其他代码更改枚举的值。示例:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
instanceof
检查。例如 ColorEnum.RED instanceof ColorEnum
(返回 true
)。您还可以使用名称 ColorEnum.fromName("RED") === ColorEnum.RED
解析实例(返回 true
)。每个实例也有一个 .name()
和一个 .ordinal()
方法,而枚举本身有一个 values()
方法,它返回一个包含所有常量的数组。
ToString()
方法。我们 JS 开发人员已经太依赖“正常工作”的东西了!此外,一个人应该能够在枚举上快速switch
。比较字符串比比较数字慢,因此如果使用字符串而不是整数,switch
的性能会稍差。
更新
我认为我下面的答案不再是用 JavaScript 编写枚举的最佳方式。有关详细信息,请参阅我的博文:Enums in 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);
}
}
或者,您可以创建 values 对象,这样您就可以吃蛋糕了:
var SIZE = {
SMALL : {value: 0, name: "Small", code: "S"},
MEDIUM: {value: 1, name: "Medium", code: "M"},
LARGE : {value: 2, name: "Large", code: "L"}
};
var currentSize = SIZE.MEDIUM;
if (currentSize == SIZE.MEDIUM) {
// this alerts: "1: Medium"
alert(currentSize.value + ": " + currentSize.name);
}
在 JavaScript 中,由于它是一种动态语言,因此甚至可以稍后将枚举值添加到集合中:
// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};
请记住,枚举的字段(本例中的值、名称和代码)不是身份检查所必需的,只是为了方便起见。另外size属性本身的名字也不需要硬编码,也可以动态设置。因此,假设您只知道新枚举值的名称,您仍然可以毫无问题地添加它:
// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};
当然,这意味着不能再做出某些假设(例如,该值代表大小的正确顺序)。
请记住,在 JavaScript 中,对象就像地图或哈希表。一组名称-值对。您可以循环浏览它们或以其他方式操纵它们,而无需提前了解它们。
例子
for (var sz in SIZE) {
// sz will be the names of the objects in SIZE, so
// 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'
var size = SIZE[sz]; // Get the object mapped to the name in sz
for (var prop in size) {
// Get all the properties of the size object, iterates over
// 'value', 'name' and 'code'. You can inspect everything this way.
}
}
顺便说一句,如果您对命名空间感兴趣,您可能想看看我的解决方案,它为 JavaScript 提供简单但功能强大的命名空间和依赖项管理:Packages JS
底线:你不能。
你可以伪造它,但你不会得到类型安全。通常这是通过创建映射到整数值的字符串值的简单字典来完成的。例如:
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 这是一个非常好的创建枚举的库。
http://www.2ality.com/2011/10/enums.html
虽然它可能不适合枚举的所有有效用途,但它还有很长的路要走。
var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
这是我们都想要的:
function Enum(constantsList) {
for (var i in constantsList) {
this[constantsList[i]] = i;
}
}
现在您可以创建您的枚举:
var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);
通过这样做,可以以通常的方式访问常量(YesNo.YES,Color.GREEN)并且它们得到一个连续的 int 值(NO = 0,YES = 1;RED = 0,GREEN = 1,BLUE = 2)。
您还可以使用 Enum.prototype 添加方法:
Enum.prototype.values = function() {
return this.allValues;
/* for the above to work, you'd need to do
this.allValues = constantsList at the constructor */
};
编辑 - 小改进 - 现在使用可变参数:(不幸的是,它在 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');
在大多数现代浏览器中,有一种 symbol 原始数据类型可用于创建枚举。它将确保枚举的类型安全,因为 JavaScript 保证每个符号值都是唯一的,即 Symbol() != Symbol()
。例如:
const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});
为了简化调试,您可以为枚举值添加描述:
const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});
在 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
Symbol
的用途。
Object.freeze
只针对那些不接受“猴子补丁自担风险”是 JS 的社会契约这一事实的人?
𝗦𝗲𝗹𝗳-𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝘃𝗲𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝘃𝗲𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝗡𝗮𝗺𝗲𝘀
让我们直接切入问题:文件大小。此处列出的所有其他答案都会使您的缩小代码膨胀到极致。我向您介绍,为了通过缩小、性能、代码可读性、大规模项目管理和许多代码编辑器中的语法提示来尽可能减少代码大小,这是进行枚举的正确方法:下划线符号变量。
如上图和下例所示,以下是五个简单的入门步骤:
确定枚举组的名称。想一个可以描述枚举目的的名词,或者至少可以描述枚举中的条目。例如,一组表示用户可选择颜色的枚举可能比 COLORS 更好地命名为 COLORCHOICES。决定组中的枚举是互斥的还是独立的。如果互斥,则以 ENUM_ 开头每个枚举变量名称。如果独立或并排,请使用 INDEX_。对于每个条目,创建一个名称以 ENUM_ 或 INDEX_ 开头的新局部变量,然后是组名称,然后是下划线,然后是属性的唯一友好名称 添加 ENUMLENGTH_、ENUMLEN_、INDEXLENGTH_ 或 INDEXLEN_(无论是 LEN_ 还是LENGTH_ 是个人喜好)最后的枚举变量。您应该在代码中尽可能使用此变量,以确保向枚举添加额外条目并增加此值不会破坏您的代码。给每个连续的枚举变量一个比上一个大一的值,从 0 开始。这个页面上有评论说 0 不应该用作枚举值,因为 0 == null, 0 == false, 0 == "" ,和其他 JS 的疯狂。我向您提交的是,为了避免这个问题并同时提高性能,请始终使用 === 并且永远不要让 == 出现在您的代码中,除非使用 typeof(例如 typeof X == "string")。在我使用 === 的所有岁月中,我从来没有遇到过使用 0 作为枚举值的问题。如果您仍然感到不安,那么在许多情况下,可以将 1 用作 ENUM_ 枚举(但不能用于 INDEX_ 枚举)中的起始值而不会降低性能。
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_
可能适合作为索引,例如在计算每个项目的出现次数时。
常量 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
请注意,在上面的代码中,添加一种新的宠物非常容易:您只需在
𝗘𝘅𝘁𝗲𝗻𝗱𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀𝗔𝗱𝗱𝗶𝘁𝗶𝗼𝗻
此外,这种枚举语法允许清晰简洁的类扩展,如下所示。要扩展类,请将递增的数字添加到父类的
https://i.stack.imgur.com/xIHxl.png
(function(window){ "use strict"; var parseInt = window.parseInt; // 表示数组实例中的索引时使用 INDEX_ const INDEX_PIXELCOLOR_TYPE = 0, // 是一个 ENUM_PIXELTYPE INDEXLEN_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, //在表示互斥物种或类型时使用 ENUM_ ENUM_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) { 结果[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA; 结果[INDEX_ALPHACOLOR_R] = parseInt(ra wstr.substring(0,2), 16);结果[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);结果[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);结果[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16); } else if (rawstr.length === 4) { 结果[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;结果[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;结果[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;结果[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;结果[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11; } else if (rawstr.length === 6) { 结果[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;结果[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);结果[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);结果[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16); } else if (rawstr.length === 3) { 结果[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;结果[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;结果[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;结果[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11; } else { 结果[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN; } 返回结果; } // 绿色的红色部分 console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]); // 透明紫色的 alpha console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); // turquoise 的枚举数组 console.log(parseHexColor("#40E0D0")); })(自己);
(长度:2,450 字节)
有人可能会说这不如其他解决方案实用:它浪费大量空间,编写时间很长,而且它没有涂上糖语法。如果他们不缩小代码,这些人是对的。但是,没有一个理性的人会在最终产品中留下未缩小的代码。对于这个缩小,闭包编译器是我还没有找到的最好的。可以找到在线访问here。 Closure 编译器能够获取所有这些枚举数据并将其内联,从而使您的 Javascript 变得超级小,运行速度超级快。因此,使用闭包编译器进行缩小。观察。
𝗠𝗶𝗻𝗶𝗳𝘆 𝗪𝗶𝘁𝗵 𝗖𝗹𝗼𝘀𝘂𝗿𝗲 𝗖𝗼𝗺𝗽𝗶𝗹𝗲𝗿
闭包编译器能够通过推理执行一些非常令人难以置信的优化,这些优化远远超出了任何其他 Javascript 压缩器的能力。 Closure Compiler 能够内联设置为固定值的原始变量。 Closure Compiler 还能够根据这些内联值进行推断,并消除 if 语句和循环中未使用的块。
https://i.stack.imgur.com/2cadt.jpg
'使用严格';(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.日志(d(“#40E0D0”))})(自我);
(长度:605 字节)
Closure Compiler 奖励您更聪明地编码和更好地组织代码,因为虽然许多压缩器会使用更大的压缩文件大小来惩罚有组织的代码,但如果您使用技巧,Closure Compiler 能够筛选您所有的清洁度和健全性以输出更小的文件大小像变量名枚举。在这个想法中,这就是编码的圣杯:一个工具既可以帮助您的代码缩小尺寸,又可以通过训练更好的编程习惯来帮助您的大脑。
𝗦𝗺𝗮𝗹𝗹𝗲𝗿 𝗖𝗼𝗱𝗲 𝗦𝗶𝘇𝗲
现在,让我们看看没有这些枚举的等效文件有多大。
Source Without Using Enumerations(长度:1,973 字节(比枚举代码短 477 字节!))
https://i.stack.imgur.com/DX0nA.png
正如所见,没有枚举,源代码更短,但代价是更大的缩小代码。我对你一无所知;但我确信我不会将源代码合并到最终产品中。因此,这种枚举形式要优越得多,因为它会导致更小的压缩文件大小。
𝗖𝗼𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝘃𝗲 🤝 𝗕𝘂𝗴 𝗙𝗶𝘅𝗶𝗻𝗴
这种枚举形式的另一个优点是它可以用于轻松管理大型项目,而不会牺牲最小的代码大小。在与许多其他人一起处理大型项目时,明确标记和标记创建代码的变量名称可能会有所帮助,以便可以快速识别代码的原始创建者以进行协作错误修复。
𝗦𝘂𝗽𝗲𝗿𝗶𝗼𝗿 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲
此外,这种枚举形式在缩小后也快得多。在普通的命名属性中,浏览器必须使用哈希图来查找属性在对象上的位置。尽管 JIT 编译器会智能地将这个位置缓存在对象上,但由于从对象中删除较低属性等特殊情况,仍然存在巨大的开销。
但是,对于连续的非稀疏整数索引 PACKED_ELEMENTS 数组,浏览器能够跳过大部分开销,因为已经指定了内部数组中值的索引。是的,根据 ECMAScript 标准,所有属性都应该被视为字符串。然而,ECMAScript 标准的这一方面在性能方面非常误导,因为所有浏览器都对数组中的数字索引进行了特殊优化。
将上面的代码与下面的代码进行比较。
人们可能会反对带有枚举的代码似乎比带有普通对象的代码长得多,但看起来可能具有欺骗性。重要的是要记住,在使用史诗闭包编译器时,源代码大小与输出大小不成比例。观察。
上面是没有枚举的缩小代码,下面是带有枚举的缩小代码。
上面的示例表明,除了具有卓越的性能之外,枚举代码还导致更小的压缩文件大小。
𝗘𝗮𝘀𝘆 𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴
此外,这个人的个人顶部的樱桃正在使用这种枚举形式以及 Javascript 模式下的 CodeMirror 文本编辑器。 CodeMirror 的 Javascript 语法高亮模式高亮显示当前范围内的局部变量。这样,当您正确输入变量名时,您会立即知道,因为如果变量名先前使用
https://i.stack.imgur.com/sSyEB.png
常量 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) { // 不管 } if(currentColor === ENUM_COLORENUM_DNE) { // 不管 }
在上面的代码段中,您收到错误警报,因为
𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻
我认为可以肯定地说,这种枚举方法确实是最好的方法,不仅可以缩小代码大小,还可以提高性能、调试和协作。
ENUM_PET_RAT
之后附加一个新条目并相应地更新 ENUMLEN_PET
。在其他枚举系统中添加新条目可能更加困难和错误。
LEN_
条目。然后,用自己的 LEN_
条目完成子类,以便将来可以进一步扩展子类。
Minified Without Using Enumerations(长度:843 字节(238 字节比枚举代码长))
// JG = Jack Giffin
const 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 Knight
const ENUM_PL_ARRAYTYPE_UNSORTED = 0,
ENUM_PL_ARRAYTYPE_ISSORTED = 1,
ENUM_BK_ARRAYTYPE_CHUNKED = 2, // added by Bob Knight
ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
ENUMLEN_PL_COLORENUM = 4;
// later on
if(
randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
// whatever
}
/// Hashmaps are slow, even with JIT juice
var ref = {};
ref.count = 10;
ref.value = "foobar";
/// Arrays, however, are always lightning fast
const 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 juice
var a={count:10,value:"foobar"};
/// Arrays, however, are always lightning fast
var a=[10,"foobar"];
var
关键字声明,那么变量名会变为特殊颜色(默认为青色)。即使您不使用 CodeMirror,但在执行枚举名称错误的代码时,至少浏览器会抛出一个有用的 [variable name] is not defined
异常。此外,JSLint 和 Closure Compiler 等 JavaScript 工具会非常大声地告诉您何时输入错误的枚举变量名称。 CodeMirror、浏览器和各种 Javascript 工具放在一起使调试这种枚举形式非常简单且非常容易。
ENUM_COLORENUM_DNE
不存在。
colors.REED
产生 undefined
),因此拼写错误会造成难以捉摸的难题。 YEA 不区分使用枚举作为索引和 ID,导致代码混乱,一切看起来都一样。 …
使用 Javascript 代理
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);
}
}
然后通过实例化类来创建枚举:
const roles = new Enum({
ADMIN: 'Admin',
USER: 'User',
});
完整解释:
您从传统语言中获得的枚举的一个非常有益的特性是,如果您尝试访问不存在的枚举器,它们会崩溃(抛出编译时错误)。
除了冻结模拟的枚举结构以防止意外/恶意添加附加值之外,其他答案都没有解决枚举的内在特征。
您可能知道,在 JavaScript 中访问不存在的成员只会返回 undefined
而不会破坏您的代码。由于枚举数是预定义的常量(即星期几),所以永远不应该存在未定义枚举数的情况。
不要误会,JavaScript 在访问未定义属性时返回 undefined
的行为实际上是语言的一个非常强大的功能,但当您尝试模拟传统的 Enum 结构时,它并不是您想要的功能。
这就是代理对象大放异彩的地方。随着 ES6 (ES2015) 的引入,代理在语言中被标准化。这是来自 MDN 的描述:
Proxy 对象用于定义基本操作的自定义行为(例如属性查找、赋值、枚举、函数调用等)。
与 Web 服务器代理类似,JavaScript 代理能够拦截对对象的操作(使用“陷阱”,如果您愿意,可以称它们为钩子)并允许您在它们完成之前执行各种检查、操作和/或操作(或在某些情况下,当我们尝试引用一个不存在的枚举器时,完全停止操作,这正是我们想要做的)。
这是一个人为的例子,它使用 Proxy 对象来模仿 Enums。本例中的枚举器是标准的 HTTP 方法(即“GET”、“POST”等):
// 创建枚举的类(13 行) // 随意将其添加到您的实用程序库中 // 您的代码库和利润!注意:由于 Proxies 是 ES6 // 特性,一些浏览器/客户端可能不支持它,并且 // 您可能需要使用类似 babel class Enum { // Enum 类实例化 JavaScript 代理对象的服务进行转译。 // 实例化一个 `Proxy` 对象需要两个参数, // 一个 `target` 对象和一个 `handler`。我们首先定义处理程序, // 然后使用处理程序实例化一个 Proxy。 // 代理处理程序只是一个对象,其属性 // 是定义代理行为的函数 // 当对其执行操作时。 // 对于枚举,我们需要定义行为,让我们检查 // 正在访问的枚举器以及正在设置的枚举器。这可以通过 // 定义“get”和“set”陷阱来完成。 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 httpMethods = new Enum({ DELETE: "DELETE", GET: "GET", OPTIONS: "OPTIONS", PATCH: "PATCH", POST: "POST", PUT: "PUT" }) // 完整性检查控制台。 log(httpMethods.DELETE) // 记录 "DELETE" try { httpMethods.delete = "delete" } catch (e) { console.log("Error: ", e.message) } // 抛出 "Cannot add/update properties在定义后的枚举实例上” try { console.log(httpMethods.delete) } catch (e) { console.log("Error: ", e.message) } // throws "No such enumerator: delete"
旁白:代理到底是什么?
我记得当我第一次开始到处看到代理这个词时,很长一段时间对我来说绝对没有意义。如果你现在就是这样,我认为概括代理的一种简单方法是将它们视为软件、机构,甚至是充当两台服务器、公司或人员之间的中间人或中间人的人。
valueOf
方法。但是,为什么要以这种方式访问它而不是仅通过点符号访问它呢?
logLevelEnum[settings.get("log_level")]
?添加 parseOrThrow
只会重复代理陷阱已经为您做的事情。
我一直在玩这个,因为我喜欢我的枚举。 =)
使用 Object.defineProperty
我想我想出了一个可行的解决方案。
这是一个 jsfiddle:http://jsfiddle.net/ZV4A6/
使用此方法.. 您应该(理论上)能够调用和定义任何对象的枚举值,而不会影响该对象的其他属性。
Object.defineProperty(Object.prototype,'Enum', {
value: function() {
for(i in arguments) {
Object.defineProperty(this,arguments[i], {
value:parseInt(i),
writable:false,
enumerable:true,
configurable:true
});
}
return this;
},
writable:false,
enumerable:false,
configurable:false
});
由于属性 writable:false
,这 应该 使其类型安全。
因此,您应该能够创建自定义对象,然后对其调用 Enum()
。分配的值从 0 开始,每个项目递增。
var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED; // == 0
EnumColors.BLUE; // == 1
EnumColors.GREEN; // == 2
EnumColors.YELLOW; // == 3
return this;
,您可以这样做:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
false
是 writable
、enumerable
和 configurable
的默认值。无需咀嚼默认值。
我知道这是一个旧的,但它通过 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]
,而不管声明的顺序如何。
enum MyEnum { Foo, Bar, Foobar }
在 ES7 中,您可以根据静态属性执行优雅的 ENUM:
class ColorEnum {
static RED = 0 ;
static GREEN = 1;
static BLUE = 2;
}
然后
if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}
优点(使用类而不是文字对象)是拥有一个父类 Enum
,然后您的所有枚举都将扩展该类。
class ColorEnum extends Enum {/*....*/}
new ColorEnum()
完全没有意义。
Enum
上有标准的 static 枚举器方法,如 getValues()
、getNames()
、iterate()
等。如果是这样,你就不要不必为每种新的 enum
重新实现它们。
创建一个对象字面量:
const Modes = {
DRAGGING: 'drag',
SCALING: 'scale',
CLICKED: 'click'
};
Object.freeze
。它防止 Closure Compiler 内联对象。
这是我使用的解决方案。
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
});
我通常使用最后两种方法从消息对象映射枚举。
这种方法的一些优点:
易于声明枚举
轻松访问您的枚举
您的枚举可以是复杂类型
如果您经常使用 getByValue,则 Enum 类具有一些关联缓存
一些缺点:
那里正在进行一些混乱的内存管理,因为我保留了对枚举的引用
仍然没有类型安全
如果您使用 Backbone,则可以使用 Backbone.Collection 免费获得完整的枚举功能(按 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()
你的答案太复杂了
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
我修改了 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');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true
我想出了以 Java 中的枚举为模型的 this 方法。这些是类型安全的,因此您也可以执行 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
}
})();
freeze
方法以实现向后兼容性?例如,if (Object.freeze) { Object.freeze(values); }
var ColorEnum = {
red: {},
green: {},
blue: {}
}
您无需确保不会以这种方式将重复的数字分配给不同的枚举值。一个新对象被实例化并分配给所有枚举值。
IE8 不支持 freeze() 方法。
来源: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
}
我对任何答案都不满意,所以我制作了又一个枚举(是的!)。
这个实现:
使用更多最新的 JS
只需声明这一类即可轻松创建枚举
按名称 (colors.RED)、字符串 (colors["RED"]) 和索引 (colors[0]) 进行映射,但您只需将字符串作为数组传入
将等效的 toString() 和 valueOf() 函数绑定到每个枚举对象(如果不希望这样做,可以简单地删除它 - 虽然 JS 的开销很小)
具有可选的全局命名/按名称字符串存储
一旦创建就冻结枚举对象,使其无法修改
特别感谢 Andre 'Fi''s answer 的启发。
代码:
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; //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
最简单的解决方案:
创造
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
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'
代码:
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(', ') }
}
这可能很有用:
const [CATS, DOGS, BIRDS] = ENUM();
实现简单高效:
function * ENUM(count=1) { while(true) yield count++ }
生成器可以生成所需的确切整数序列,而无需知道有多少常量。它还可以支持一个可选参数,该参数指定从哪个(可能是负数)数字开始(默认为 1
)。
一种快速简单的方法是:
var Colors = function(){
return {
'WHITE':0,
'BLACK':1,
'RED':2,
'GREEN':3
}
}();
console.log(Colors.WHITE) //this prints out "0"
以下是实现 TypeScript enums 的几种不同方法。
最简单的方法是迭代一个对象,向对象添加反向键值对。唯一的缺点是您必须手动设置每个成员的值。
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
这里有一个 lodash mixin,用于使用字符串创建枚举。虽然这个版本涉及更多一点,但它会自动为您进行编号。本示例中使用的所有 lodash 方法都有一个常规的 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
我刚刚发布了一个 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 true
console.log(curMode.isSignUp()); // output false
console.log(curMode.isForgotPassword()); // output false
这个小工具的一个好处是在现代环境(包括 nodejs 和 IE 9+ 浏览器)中,返回的 Enum 对象是不可变的。
如需更多信息,请查看https://github.com/greenlaw110/enumjs
更新
我废弃了 gen_enum
包并将函数合并到 constjs 包中,它提供了更多功能,包括不可变对象、JSON 字符串反序列化、字符串常量和位图生成等。查看 https://www.npmjs.com/package/constjs 了解更多信息
要从 gen_enum
升级到 constjs
,只需更改语句
var genEnum = require('gen_enum');
至
var genEnum = require('constjs').enum;
我创建了一个 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>..."
尽管 ES2015 支持 only static methods(而不是静态属性)(参见 here,第 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 导入模块时,它也能按预期工作。
与大多数其他答案相比,此方法的优势在于您可以将它与静态类型检查器一起使用(例如 Flow),并且您可以在开发时使用静态类型检查断言您的变量,参数等是特定的 CellState
“枚举”而不是其他一些枚举(如果您使用通用对象或符号,则无法区分)。
更新
上面的代码有一个缺陷,它允许创建额外的 CellState
类型的对象(即使因为它被冻结了,所以不能将它们分配给 CellState
的静态字段)。尽管如此,以下更精炼的代码仍具有以下优点:
不能再创建 CellState 类型的对象 您可以保证不会为两个枚举实例分配相同的代码实用程序方法来从字符串表示中获取枚举 返回枚举的所有实例的值函数不必创建返回上面的值,手动(且容易出错)的方式。 '使用严格'; class Status { constructor(code, displayName = code) { if (Status.INSTANCES.has(code)) throw new Error(`duplicate code value: [${code}]`); if (!Status.canCreateMoreInstances) throw new Error(`在创建所有静态实例后尝试调用构造函数(${code}`+ `, ${displayName})`); this.code = 代码; this.displayName = 显示名称; Object.freeze(this); Status.INSTANCES.set(this.code, this); } toString() { return `[code: ${this.code}, displayName: ${this.displayName}]`; } 静态实例 = 新地图();静态 canCreateMoreInstances = true; // 值:静态 ARCHIVED = new Status('Archived');静态观察=新状态('观察');静态 SCHEDULED = new Status('Scheduled');静态 UNOBSERVED = new Status('Unobserved');静态 UNTRIGGERED = new Status('Untriggered');静态值 = function() { return Array.from(Status.INSTANCES.values()); } static fromCode(code) { if (!Status.INSTANCES.has(code)) throw new Error(`unknown code: ${code}`);否则返回 Status.INSTANCES.get(code); } } Status.canCreateMoreInstances = false;对象.冻结(状态);出口.状态=状态;
这就是 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
你可以做这样的事情
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 DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });
。您无需指定 id,只需使用空对象来比较枚举即可。if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
if (Object.freeze) { Object.freeze(DaysEnum); }
({ monday: {},
等意味着如果你通过 stringify 将该对象转换为 JSON,你将得到[{"day": {}}]
,这是行不通的。var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
。在我之前的评论中比较对象比比较数字要慢得多。