在 JavaScript 中定义枚举的首选语法是什么?

在 JavaScript 中定义枚举的首选语法是什么?就像是:

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

// later on

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

还是有更好的成语?

答案

从 1.8.5 开始,可以密封和冻结对象,因此将以上定义为:

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

要么

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

和瞧! JS 枚举。

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

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

确保类型安全性(使用枚举或其他方式)的程度更高的一种方法是使用诸如TypeScriptFlow 之类的工具。

资源

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

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

话虽这么说,因为值无关紧要(您使用过 0、1、2),所以如果您想输出当前值,我将使用有意义的字符串。

更新

感谢大家的支持,但我认为下面的答案不再是用 JavaScript 编写枚举的最佳方法。有关更多详细信息,请参见我的博客文章: JavaScript 中的 Enums


警报名称已经可以:

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 中,对象就像是maphash table 。一组名称 / 值对。您可以循环浏览它们或以其他方式操纵它们,而无需事先了解它们。

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 呢?这样是否可以防止您将星期一设置为星期四? – Fry Quad

绝对, Object.freeze将完全解决我抱怨的问题。我想提醒大家,当我写上面的文章时, Object.freeze实际上并不存在。

现在.... 现在它提供了一些非常有趣的可能性。

编辑 2
这是一个用于创建枚举的很好的库。

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

尽管它可能不适合枚举的所有有效用法,但它却走了很长一段路。

这就是我们所有人想要的:

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

通过这样做,可以按常规方式访问常量(是,是,Color.GREEN),并且它们会得到一个连续的整数值(否 = 0,是 = 1;红色 = 0,绿色 = 1,蓝色 = 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 */
};


编辑 - 小改进 - 现在使用 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');

在大多数现代浏览器中,有一种符号原始数据类型可用于创建枚举。这将确保枚举的类型安全,因为 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

𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦™𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦

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


wvwvvvvvvvvv

下划线符号变量

const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;

// later on

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

这就是我记得何时使用INDEX_和何时使用ENUM_

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

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

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

观察一下,在上面的代码中,添加一种新的宠物真的很容易:您只需要在ENUM_PET_RAT之后添加一个新条目并相应地更新ENUMLEN_PET 。在其他枚举系统中添加新条目可能会更加困难且容易出错。


wvwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwvvw wvwvwvw wvwvw wvwvw

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

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

附加扩展图

(function(window){
    "use strict";
    var parseInt = window.parseInt

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a 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,
    // use ENUM_ when representing a mutually-exclusive species or type
          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) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

    // the red component of green
    console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]);
    // the alpha of transparent purple
    console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); 
    // the enumerated array for turquoise
    console.log(parseHexColor("#40E0D0"));
})(self);

(长度:2,450 字节)

有人可能会说这比其他解决方案不太实用:它占用了很多空间,需要花费很长时间来编写,并且没有使用糖语法。如果他们不缩减代码,那么这些人将是正确的。但是,没有理性的人会在最终产品中留下未缩小的代码。对于这种缩小,Closure Compiler 是我尚未找到的最好的。在线访问可以在这里找到。 Closure 编译器能够获取所有这些枚举数据并将其内联,从而使您的 Javascript 变得超级精简,并且能够快速运行超级精简。因此,使用 Closure Compiler 进行压缩。观察一下。


wvwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwvvw wvwvwvw wvwvw wvwvw

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

Closure 编译器能够通过推理执行一些非常令人难以置信的优化,这些推理远远超出了其他 Javascript 缩小器的能力。 Closure Compiler 能够内联设置为固定值的基本变量。 Closure Compiler 还能够基于这些内联值进行推断,并消除 if 语句和循环中未使用的块。

通过Closure编译器拧代码

'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);

(长度:605 字节)


wvwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwvvw wvwvwvw wvwvw wvwvw

𝗦𝗶𝘇𝗲

现在,让我们看看没有任何这些枚举的等效文件将有多大。

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

代码大小表



可以看出,没有枚举,源代码会更短,但代价是较大的缩小代码。我不了解你; 但是我确定我不会将源代码包含到最终产品中。因此,这种枚举形式非常优越,因为它导致较小的缩小文件大小。


wvwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwvvw wvwvwvw wvwvw wvwvw

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

这种枚举形式的另一个优点是,它可用于轻松管理大型项目,而无需牺牲最小化的代码大小。在与许多其他人一起从事大型项目时,最好使用创建代码的人来显式标记和标记变量名,这样可以快速识别出代码的原始创建者以进行协作的错误修复,这可能是有益的。

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

𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

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

但是,使用连续的非稀疏整数索引的PACKED_ELEMENTS数组,由于已经指定了内部数组中值的索引,因此浏览器可以跳过很多开销。是的,根据 ECMAScript 标准,所有属性都应视为字符串。尽管如此,ECMAScript 标准的这一方面在性能上还是极具误导性,因为所有浏览器都对数组中的数字索引进行了特殊的优化。


wvwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwvvw wvwvwvw wvwvw wvwvw

𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

此外,我个人 喜欢的是在 Javascript 模式下将这种枚举形式与CodeMirror文本编辑器一起使用。 CodeMirror 的 Javascript 语法突出显示模式突出显示当前作用域中的局部变量。这样,当您正确键入变量名称时,您会立即知道,因为如果以前使用var关键字声明了变量名称,则变量名称会变成特殊的颜色(默认为青色)。即使您不使用 CodeMirror,执行带有错误类型的枚举名称的代码时,至少浏览器也会抛出有用的<em>[variable name]</em> is not defined异常。同样,JavaScript 工具(例如 JSLint 和 Closure Compiler)在告诉您何时键入枚举变量名称时非常吵闹。 CodeMirror,浏览器和各种 Javascript 工具一起使调试这种枚举形式变得非常简单而且非常容易。

CodeMirror高亮演示

const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;
var currentColor = ENUM_COLORENUM_GREEN;

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

if(currentColor === ENUM_COLORENUM_DNE) {
   // whatever
}

在上述代码段中,由于不存在ENUM_COLORENUM_DNE ,因此向您发出错误ENUM_COLORENUM_DNE


wvwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwvvw wvwvwvw wvwvw wvwvw

☑☑

因此,我得出结论,确实,这种枚举形式不仅是减小代码大小的最佳方法,而且还是性能,调试和协作的最佳方法。

阅读了有帮助的问题后,我感谢作者通过在问题框中单击左上角的箭头将时间投入到他们的写作中。每个答案框也具有这些向上箭头之一。

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

我想使用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

使用 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” 等):

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


ASIDE:代理到底是什么?

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

我知道这是一个古老的方法,但是此后通过 TypeScript 接口实现的方法是:

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

这使您可以查找两个MyEnum.Bar返回 1,和MyEnum[1]返回 “酒吧”,无论声明的顺序。