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

2020/09/15 04:02 · javascript ·  · 0评论

在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

𝗦𝗲𝗹𝗳-𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝘃𝗲

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


下划线符号变量

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

  1. 确定枚举组的名称。考虑一个可以描述枚举目的或至少枚举条目的名词。例如,代表用户可以选择的颜色的一组枚举可能比COLORS更好地命名为COLORCHOICES。
  2. 确定组中的枚举是互斥的还是独立的。如果互斥,则以开头的每个枚举变量名ENUM_如果独立或并排使用,请使用INDEX_
  3. 对于每个条目,创建一个新的局部变量,其名称以ENUM_开头INDEX_,然后是组的名称,然后是下划线,然后是属性的唯一友好名称。
  4. 添加ENUMLENGTH_ENUMLEN_INDEXLENGTH_,或者INDEXLEN_(无论是LEN_LENGTH_在最后是个人喜好)枚举变量。您应该在代码中尽可能使用此变量,以确保向枚举添加额外的条目并递增此值不会破坏您的代码。
  5. 给每一个连续变量枚举值一个超过最后,从0开始。有迹象表明,说这个页面的评论0不应该被用来作为一个枚举值,因为0 == null0 == false0 == "",和其他JS疯狂。我谨向您指出,为避免此问题并同时提高性能,请始终使用===并且==除非typeof(ex 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_在某些情况下,例如当计算每个项目的出现次数时,它可能适合作为索引。

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在其他枚举系统中添加新条目可能会更加困难且容易出错。


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

另外,枚举的这种语法允许清晰简洁的类扩展,如下所示。要扩展类,请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_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            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进行压缩。观察一下。


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

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字节)

Closure Compiler会奖励您更聪明地编码和组织代码,因为许多压缩工具会以较小的文件大小来惩罚有组织的代码,而Closure Compiler可以筛选出所有的整洁度,如果您使用技巧也能输出较小的文件大小像变量名枚举一样。在这种情况下,这就是编码的圣杯:一种既可以以较小的最小尺寸协助您的代码,又可以通过训练更好的编程习惯来协助您的思想的工具。


𝗦𝗶𝘇𝗲

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

不使用枚举的源(长度:1,973字节(比枚举的代码短477字节!))
最小尺寸,不使用枚举(长度: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
}

𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

此外,这种枚举形式在最小化后也要快得多。在普通的命名属性中,浏览器必须使用哈希图来查找该属性在对象上的位置。尽管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";

一个人可能会反对带有枚举的代码,而枚举似乎要比带有普通对象的代码长得多,但看起来可能是骗人的。重要的是要记住,使用史诗级的Closure Compiler时,源代码的大小与输出的大小不成比例。观察一下。

/// Hashmaps are slow, even with JIT juice
var a={count:10,value:"foobar"};

没有枚举的最小化代码在上方,而带有枚举的最小化代码在下方。

/// Arrays, however, are always lightning fast
var a=[10,"foobar"];

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


𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

此外,这种一个人的个人在上面的樱桃是用这种形式与沿枚举CodeMirror在Javascript模式文本编辑器。CodeMirror的Javascript语法突出显示模式突出显示当前作用域中的局部变量。这样,当您正确键入变量名称时,您会立即知道,因为如果以前使用var关键字声明了变量名称,则变量名称会变成特殊的颜色(默认为青色)。即使您不使用CodeMirror,也至少浏览器会抛出一个有用的信息。[variable name] 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不存在错误,您收到了警报


𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻

可以肯定地说,这种枚举方法确实是最佳的方法,不仅是为了减小代码大小,而且还是性能,调试和协作。

使用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:代理到底是什么?

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

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

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

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

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

这样MyEnum.Bar一来,MyEnum[1]无论声明的顺序如何,都可以查找返回1以及返回“ Bar”的两个值。

ES7中,您可以依靠静态属性进行优雅的ENUM:

class ColorEnum  {
    static RED = 0 ;
    static GREEN = 1;
    static BLUE = 2;
}

然后

if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}

优点(使用类而不是文字对象)是拥有父类,Enum然后您所有的Enums都将扩展该类。

 class ColorEnum  extends Enum {/*....*/}

这是我使用的解决方案。

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类具有一些关联缓存

一些缺点:

  • 在这里进行一些混乱的内存管理,因为我保留了对枚举的引用
  • 仍然没有类型安全

创建一个对象文字:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};

如果您使用的骨干,你可以得到全面的枚举功能免费使用(通过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()

你的答案太复杂了

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枚举为模型。这些是类型安全的,因此您也可以执行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
    }

})();

IE8不支持Frozen()方法。

来源:
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
}
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

您无需确保不会以这种方式将重复的数字分配给不同的枚举值。实例化一个新对象并将其分配给所有枚举值。

这是实现TypeScript枚举的几种不同方法

最简单的方法是仅迭代一个对象,向该对象添加反转的键值对。唯一的缺点是您必须手动设置每个成员的值。

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_enumconstjs仅更改语句

var genEnum = require('gen_enum');

var genEnum = require('constjs').enum;

最简单的解决方案:

创造

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

我制作了一个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#中的Enums):

var val2 = enum1.item2;

要获取值的名称(将相同的值用于不同的名称时可能会模棱两可):

var name1 = enum1.GetName(0);  // "item1"

要获取具有对象中每个名称和值的数组:

var arr = enum1.GetObjArr();

将产生:

[{ Name: "item1", Value: 0}, { ... }, ... ]

您还可以轻松获取html select选项:

var html = enum1.GetSelectOptionsHTML();

其中:

"<option value='0'>item1</option>..."

尽管只有静态方法(而不是静态的属性)在ES2015(见支持这里为好,§15.2.2.2),奇怪的是您可以用使用下面巴贝尔的es2015预设:

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由于冻结而无法将它们分配给的静态字段)。尽管如此,以下更完善的代码仍具有以下优点:

  1. 不能CellState再创建类型的对象
  2. 您可以确保没有为两个枚举实例分配相同的代码
  3. 从字符串表示形式获取枚举的实用方法
  4. values返回枚举的所有实例函数不必以上述手动(且容易出错)的方式创建返回值。

    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;

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(', ') }

}

这是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 Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

console.log(Colors.WHITE)  //this prints out "0"

截至撰写本文时(201410月) -这是一种当代解决方案。我正在将解决方案编写为节点模块,并包括使用Mocha和Chai以及underscoreJS进行的测试。您可以轻松地忽略它们,如果需要的话,只需使用Enum代码即可。

看过很多文章,它们的库太复杂了。在Java中获得枚举支持的解决方案是如此简单,实际上并不需要。这是代码:

档案:enums.js

_ = require('underscore');

var _Enum = function () {

   var keys = _.map(arguments, function (value) {
      return value;
   });
   var self = {
      keys: keys
   };
   for (var i = 0; i < arguments.length; i++) {
      self[keys[i]] = i;
   }
   return self;
};

var fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));
var encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));

exports.fileFormatEnum = fileFormatEnum;
exports.encodingEnum = encodingEnum;

并进行测试以说明它能为您提供什么:

文件:enumsSpec.js

var chai = require("chai"),
    assert = chai.assert,
    expect = chai.expect,
    should = chai.should(),
    enums = require('./enums'),
    _ = require('underscore');


describe('enums', function () {

    describe('fileFormatEnum', function () {
        it('should return expected fileFormat enum declarations', function () {
            var fileFormatEnum = enums.fileFormatEnum;
            should.exist(fileFormatEnum);
            assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');
            assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');
        });
    });

    describe('encodingEnum', function () {
        it('should return expected encoding enum declarations', function () {
            var encodingEnum = enums.encodingEnum;
            should.exist(encodingEnum);
            assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format');
            assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');
        });
    });

});

如您所见,您将获得一个Enum工厂,只需调用enum.keys就可以获取所有键,并且可以将键本身与整数常量进行匹配。您可以使用不同的值重用工厂,并使用Node的模块化方法导出那些生成的枚举。

再说一次,如果您只是一个临时用户,或者在浏览器中,只需将代码的工厂部分包括在内,如果您不希望在代码中使用下划线库,也可能会删除下划线库。

我认为它很容易使用。https://stackoverflow.com/a/32245370/4365315

var A = {a:11, b:22}, 
enumA = new TypeHelper(A);

if(enumA.Value === A.b || enumA.Key === "a"){ 
... 
}

var keys = enumA.getAsList();//[object, object]

//set
enumA.setType(22, false);//setType(val, isKey)

enumA.setType("a", true);

enumA.setTypeByIndex(1);

更新:

有我的助手代码(TypeHelper)。

本文地址:http://javascript.askforanswer.com/zaijavascriptzhongdingyimeijudeshouxuanyufashishenme.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!