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

问:

以下是否会使对象满足枚举在 JavaScript 中的所有特征?就像是:

my.namespace.ColorEnum = {
  RED : 0,
  GREEN : 1,
  BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
  // whatever
}


还是有其他方法可以做到这一点?

答1:

huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式

从 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 这样的工具。

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

根据 Wikipedia (en.wikipedia.org/wiki/JavaScript#Versions),它适用于 Firefox 4、IE 9、Opera 11.60,我知道它适用于 Chrome。

这是 2012 年现在的正确答案。更简单:var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });。您无需指定 id,只需使用空对象来比较枚举即可。 if (incommingEnum === DaysEnum.monday) //incommingEnum is monday

为了向后兼容,if (Object.freeze) { Object.freeze(DaysEnum); }

我想指出,做 ({ monday: {}, 等意味着如果你通过 stringify 将该对象转换为 JSON,你将得到 [{"day": {}}] ,这是行不通的。

@Supuhstar我现在对这个问题的看法不同了。不要使用 freeze(),它完全没用,而且浪费时间做“愚蠢”的事情。如果你想公开一个枚举,只需公开这个:var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}。在我之前的评论中比较对象比比较数字要慢得多。

答2:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

这不是一个很好的答案,但我个人认为这很好用

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

这在另一个答案中有所说明,但由于此答案是公认的答案,因此我将在此处发布。 OP的解决方案是正确的。但是,如果与 Object.freeze() 一起使用,效果会更好。这将防止其他代码更改枚举的值。示例:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});

@TolgaE 谢谢你的图书馆!它启发了我不仅将其归结为最低限度,而且还添加了一些功能!我已经把你的分叉了,把它都放在这里了:github.com/BlueHuskyStudios/Micro-JS-Enum

@Supuhstar 太好了!我很高兴你可以使用它。如果你想将它合并到这个库中,请随时提出拉取请求,然后我可以更新 npm 库

如果有人感兴趣,我有 implemented 类型安全枚举,类似于它们在 Java 中的方式。这意味着您可以进行 instanceof 检查。例如 ColorEnum.RED instanceof ColorEnum(返回 true)。您还可以使用名称 ColorEnum.fromName("RED") === ColorEnum.RED 解析实例(返回 true)。每个实例也有一个 .name() 和一个 .ordinal() 方法,而枚举本身有一个 values() 方法,它返回一个包含所有常量的数组。

我不确定我是否同意“有意义的字符串”的建议。不应将枚举视为字符串或数字;它们是抽象数据类型。如果没有一些辅助方法,应该不可能“输出当前值”。在 Java 和 .NET 中,它是 ToString() 方法。我们 JS 开发人员已经太依赖“正常工作”的东西了!此外,一个人应该能够在枚举上快速switch。比较字符串比比较数字慢,因此如果使用字符串而不是整数,switch 的性能会稍差。

答3:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

更新

我认为我下面的答案不再是用 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

那么如果你只有它的名字,你将如何去创建一个简单的 SIZE 呢?

@Johanisma:该用例对枚举没有真正意义,因为它们的整个想法是您提前知道所有值。但是,没有什么能阻止您稍后在 Javascript 中添加额外的值。我将在我的答案中添加一个示例。

使用属性方法为您的帖子的链接+1。优雅之处在于基本声明很简单,就像在 OP 中一样,在需要时添加了属性功能。

@Stijin,真的很喜欢您更新的解决方案。在您的博客上的评论中发布代码,并作为下面的评论。基本上,使用一个函数,从现有的哈希列表执行属性构建,并可选择冻结它(我的列表中的 mkenum_2)。干杯。

还有一个实现它的库,还包括比较和反向搜索的不错功能:github.com/adrai/enum

答4:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

底线:你不能。

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

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

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

javascript中有类型安全吗?

所以不要将值映射到对象属性。使用 getter 访问枚举数(存储为“私有”对象的属性)。一个简单的实现看起来像 - var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1

@Scott Evernden:要点。 @kangax:关键是它仍然是一个黑客。枚举根本不存在于 Javascript、句号、故事结尾。即使是 Tim Sylvester 建议的模式,仍然不是理想的 hack。

用文字洒在代码上不是很容易维护,因此为它创建常量是有意义的。当然 Javascript 也没有常量。所以基本上这只是一种编写干净代码的方法。它不能被强制执行,但在 Javascript 中却不多。您可以重新定义常量或函数,或者大部分内容。 EG:document.getElementById = function() {alert("你搞砸了。Javascript 不是类型安全的。");};

@Randolpho:Artur Czajka 的 Object.freeze 怎么样?这不会阻止您将星期一设置为星期四吗?

答5:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

这是我们都想要的:

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

@Marquizzo(和 OP)我根据这个答案创建了一个改进版本:stackoverflow.com/a/60309416/1599699

@Andrew 我创建了一个单独的、经过深思熟虑、经过仔细考虑和彻底审查的答案,我在生产中多次使用过该答案:stackoverflow.com/a/50355530/5601591

答6:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

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

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

为了简化调试,您可以为枚举值添加描述:

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

Plunker demo

在 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

这是理论上的正确答案。实际上,2015 浏览器支持还远远不够。到目前为止还没有准备好生产。

尽管尚不支持浏览器,但这是最佳答案,因为这接近 Symbol 的用途。

嗯...枚举值通常需要可序列化,而符号序列化和反序列化并不那么方便。

只是我还是Object.freeze只针对那些不接受“猴子补丁自担风险”是 JS 的社会契约这一事实的人?

答7:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

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

让我们直接切入问题:文件大小。此处列出的所有其他答案都会使您的缩小代码膨胀到极致。我向您介绍,为了通过缩小、性能、代码可读性、大规模项目管理和许多代码编辑器中的语法提示来尽可能减少代码大小,这是进行枚举的正确方法:下划线符号变量。

如上图和下例所示,以下是五个简单的入门步骤:

确定枚举组的名称。想一个可以描述枚举目的的名词,或者至少可以描述枚举中的条目。例如,一组表示用户可选择颜色的枚举可能比 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; iENUM_PET_RAT 之后附加一个新条目并相应地更新 ENUMLEN_PET。在其他枚举系统中添加新条目可能更加困难和错误。

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

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

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]=17c(a[0 ],16),b[2]=17c(a[1],16),b[3]=17c(a[2],16),b[4]=17c(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 ]=17c(a[0],16),b[2]=17c(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 字节!)) Minified Without Using Enumerations(长度:843 字节(238 字节比枚举代码长))

https://i.stack.imgur.com/DX0nA.png

正如所见,没有枚举,源代码更短,但代价是更大的缩小代码。我对你一无所知;但我确信我不会将源代码合并到最终产品中。因此,这种枚举形式要优越得多,因为它会导致更小的压缩文件大小。

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

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

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

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

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

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

/// 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"];

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

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

此外,这个人的个人顶部的樱桃正在使用这种枚举形式以及 Javascript 模式下的 CodeMirror 文本编辑器。 CodeMirror 的 Javascript 语法高亮模式高亮显示当前范围内的局部变量。这样,当您正确输入变量名时,您会立即知道,因为如果变量名先前使用 var 关键字声明,那么变量名会变为特殊颜色(默认为青色)。即使您不使用 CodeMirror,但在执行枚举名称错误的代码时,至少浏览器会抛出一个有用的 [variable name] is not defined 异常。此外,JSLint 和 Closure Compiler 等 JavaScript 工具会非常大声地告诉您何时输入错误的枚举变量名称。 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_COLORENUM_DNE 不存在。

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻

我认为可以肯定地说,这种枚举方法确实是最好的方法,不仅可以缩小代码大小,还可以提高性能、调试和协作。

huntsbot.com – 高效赚钱,自由工作

嗯。比起代码大小,我更喜欢可读性、易用性和理解性。

@Andrew 根据我的回答,您可以两者兼得。我的答案是最容易使用/管理的代码和最小的缩小代码大小。🙂

@Andrew我已尝试将您的 Yet Another Enum (YEA!) 应用于我的答案中的颜色解析器示例。但是,我发现了一些您可能想要解决的问题。 YEA 无法使用子类扩展枚举,这迫使我创建单独的父类和子类,这在大型项目中可能很难管理。 YEA 不能确保条目存在(例如 colors.REED 产生 undefined),因此拼写错误会造成难以捉摸的难题。 YEA 不区分使用枚举作为索引和 ID,导致代码混乱,一切看起来都一样。 …

@Andrew ... YEA 阻碍了 Closure Compiler 的缩小能力。将带有 YEA(3549 字节)的源代码与带有 YEA(1344 字节)的缩小代码与我的解决方案(604 字节)进行比较。最后,YEA 涉及“按名称映射”,因为它将字符串名称与枚举 ID 分开。我的只考虑 ID,所以不需要“按名称映射”,从而使设计更简单,性能更好。感谢您分享您的解决方案,但它需要许多修复才能实用。

过多的帖子格式和代码作为图像。会推荐编辑。

答8:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

使用 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”

旁白:代理到底是什么?

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

如何做类似 myEnum.valueOf("someStringValue") 的事情?预期:如果输入字符串具有枚举器元素的值,则应返回该项目。如果没有项目具有该字符串值,则抛出异常。

@sscarduzio 您可以通过将其指定为 Enum 类的实例方法来覆盖默认的 valueOf 方法。但是,为什么要以这种方式访问它而不是仅通过点符号访问它呢?

我的枚举是 const logLevelEnum = new Enum({ INFO: "info", DEBUG: "debug"}) 并且我从输入中解析任意字符串“info”或“debug”。所以我需要类似 currentLogLevel = logLevelEnum.parseOrThrow(settings.get("log_level"))

为什么你不能只做logLevelEnum[settings.get("log_level")]?添加 parseOrThrow 只会重复代理陷阱已经为您做的事情。

答9:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

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

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

我没有考虑到这一点,因为这不是我正常的做事方式。但你是绝对正确的!我会编辑进去。

我真的很喜欢这个,虽然我不喜欢搞砸对象空间(使用全局函数 ENUM)。将此转换为 mkenum 函数并添加可选的数字赋值 => var mixedUp = mkenum('BLACK', {RED: 0x0F00, BLUE: 0X0F, GREEN: 0x0F0, WHITE: 0x0FFF, ONE: 1}, TWO, THREE, FOUR) ; // 添加我的代码作为下面的答案。谢谢。

老实说,我什至不再使用它了。我一直在使用 Google 的 Closure Compiler,如果您使用 Advanced 设置,它的效果会不太好(或者它只会使事情复杂化)。所以我刚刚回到标准对象表示法。

false 是 writable、enumerable 和 configurable 的默认值。无需咀嚼默认值。

答10:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

我知道这是一个旧的,但它通过 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],而不管声明的顺序如何。

加上 MyEnum["Bar"] 可以返回 1... <3 TypeScript 到目前为止...

当然,如果您实际上正在使用 Typescript:enum MyEnum { Foo, Bar, Foobar }

答11:

huntsbot.com – 高效赚钱,自由工作

在 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() 完全没有意义。

扩展枚举听起来很疯狂,真的

一旦语言本身不支持它就有意义保持这个约定并像这样使用!我同意!

我认为(?)OP 的目的是:纯静态的好处是它可以作为单例在任何地方使用,并且您不需要实例化该类 - OP 并不建议您做!我认为他的意思是超类 Enum 上有标准的 static 枚举器方法,如 getValues()、getNames()、iterate() 等。如果是这样,你就不要不必为每种新的 enum 重新实现它们。

原文链接:https://www.huntsbot.com/qa/1j3j/how-can-i-guarantee-that-my-enums-definition-doesnt-change-in-javascript?lang=zh_CN&from=csdn

huntsbot.com – 高效赚钱,自由工作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值