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

时间:2008-11-13 19:09:07

标签: javascript syntax enums

在JavaScript中定义枚举的首选语法是什么?类似的东西:

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

// later on

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

还是有更优选的成语?

52 个答案:

答案 0 :(得分:725)

由于1.8.5可以密封和冻结对象,因此将上面定义为:

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

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)
瞧,瞧! JS枚举。

注意:我是在2011年写的,但是它是2019年 - 使用const 来防止覆盖您的枚举词典。

但是,这并不妨碍您为变量分配不需要的值,这通常是枚举的主要目标:

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

确保更强的类型安全性(使用枚举或其他方式)的一种方法是使用TypeScriptFlow等工具。

Source

不需要引用,但我保持了它们的一致性。

答案 1 :(得分:594)

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

话虽如此,因为值无关紧要(你使用过0,1,2),我会使用一个有意义的字符串,以防你输出当前值。

答案 2 :(得分:488)

更新:感谢大家的所有支持,但我认为下面的答案不再是在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);
   }
}

或者,您可以将值设为对象,这样您就可以吃蛋糕并吃掉它:

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中,对象就像地图或哈希表。一组名称 - 值对。你可以在不事先了解它们的情况下循环遍历它们或以其他方式操纵它们。

E.G:

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

答案 3 :(得分:80)

底线:你不能。

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

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

虽然它可能不适合每个有效的枚举使用,但它有很长的路要走。

答案 4 :(得分:53)

这是我们都想要的:

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


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

答案 5 :(得分:47)

在大多数现代浏览器中,有一个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

答案 6 :(得分:23)

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

使用Object.defineProperty我认为我想出了一个可行的解决方案。

这是一个jsfiddle:http://jsfiddle.net/ZV4A6/

使用此方法..您应该(理论上)能够为任何对象调用和定义枚举值,而不会影响该对象的其他属性。

Object.defineProperty(Object.prototype,'Enum', {
    value: function() {
        for(i in arguments) {
            Object.defineProperty(this,arguments[i], {
                value:parseInt(i),
                writable:false,
                enumerable:true,
                configurable:true
            });
        }
        return this;
    },
    writable:false,
    enumerable:false,
    configurable:false
}); 

由于属性writable:false应该使其类型安全。

因此,您应该能够创建自定义对象,然后在其上调用Enum()。分配的值从0开始,每个项目增加。

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

答案 7 :(得分:18)

这是我所知道的旧版本,但是之后通过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],无论声明的顺序如何。

答案 8 :(得分:18)

大多数人的"首选语法" 已在上面列出。但是,存在一个主要的首要问题:代码大小。此处列出的每个其他答案都会使您的代码大小变得极端。此外,由于涉及太多意见,因此手头的问题已被暂停。因此,我事实向您呈现,为了获得最佳性能,代码可读性,大规模项目管理,许多代码编辑器中的语法提示,以及缩小代码大小,这是正确的方法做枚举。

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

// later on

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

此外,此语法允许清晰简洁的类扩展,如下所示。

(长度:2,450字节)



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

    const ENUM_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          ENUMLEN_PIXELCOLOR   = 1,
          ENUM_SOLIDCOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_SOLIDCOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_SOLIDCOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUMLEN_SOLIDCOLOR   = ENUMLEN_PIXELCOLOR+3,
          ENUM_ALPHACOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_ALPHACOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_ALPHACOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUM_ALPHACOLOR_A    = ENUMLEN_PIXELCOLOR+3,
          ENUMLEN_ALPHACOLOR   = ENUMLEN_PIXELCOLOR+4,
          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[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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




有些人可能会说这比其他解决方案更不实用:它占用了大量空间,需要很长时间才能编写,并且没有涂上糖语法。如果他们不缩小代码,那些人就是对的。但是,没有合理的人会在最终产品中留下未经授权的代码。对于这种缩小,Closure Compiler是我还没有找到的最好的。可以在here找到在线访问权限。 Closure编译器能够获取所有这些枚举数据并将其内联,使您的Javascript变得非常小,并且可以快速运行超级duper。观察。

(长度:605字节)



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




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

Source Without Using Enumerations(长度:1,973字节(比枚举代码短477个字节!))
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
}

此外,这种形式的枚举在缩小后也要快得多。在常规命名属性中,浏览器必须使用散列图来查找属性在对象上的位置。尽管JIST编译器智能地将此位置缓存在对象上,但对于特殊情况(例如从对象中删除较低属性)仍然存在巨大的开销。  但是,对于连续的非稀疏整数索引PACKED_ELEMENTS数组,浏览器能够跳过大部分开销,因为已经指定了内部数组中的值的索引。是的,根据ECMAScript标准,所有属性都应该被视为字符串。然而,ECMAScript标准的这一方面对性能非常误导,因为所有浏览器都对数组中的数字索引进行了特殊优化。

此外,我在顶部的个人 cherry 在Javascript模式下使用这种形式的枚举以及CodeMirror文本编辑器。 CodeMirror的Javascript语法突出显示模式突出显示当前范围中的局部变量。这样,当您正确输入变量名称时,您立即知道,因为如果先前使用var关键字声明变量名称,则变量名称将变为特殊颜色(默认为青色)。即使您不使用CodeMirror,在执行包含错误的枚举名称的代码时,至少浏览器会抛出一个有用的[variable name] is not defined异常。此外,JavaScript工具(如JSLint和Closure Compiler)非常响亮地告诉您何时输入枚举变量名称。 CodeMirror,浏览器和各种Javascript工具放在一起使调试这种枚举形式变得非常简单和容易。

因此,我得出结论,实际上,这种枚举形式不仅适用于缩小代码大小,而且适用于性能,清晰度和协作。

答案 9 :(得分:17)

使用Javascript Proxies

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

完整说明:

从传统语言中获得的Enums的一个非常有用的功能是,如果您尝试访问不存在的枚举器,它们会爆炸(抛出编译时错误)。

除了冻结模拟的枚举结构以防止意外/恶意添加其他值之外,其他任何答案都没有解决Enums的内在特性。

您可能已经知道,在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:什么是代理?

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

答案 10 :(得分:15)

这是我使用的解决方案。

function Enum() {
    this._enums = [];
    this._lookups = {};
}

Enum.prototype.getEnums = function() {
    return _enums;
}

Enum.prototype.forEach = function(callback){
    var length = this._enums.length;
    for (var i = 0; i < length; ++i){
        callback(this._enums[i]);
    }
}

Enum.prototype.addEnum = function(e) {
    this._enums.push(e);
}

Enum.prototype.getByName = function(name) {
    return this[name];
}

Enum.prototype.getByValue = function(field, value) {
    var lookup = this._lookups[field];
    if(lookup) {
        return lookup[value];
    } else {
        this._lookups[field] = ( lookup = {});
        var k = this._enums.length - 1;
        for(; k >= 0; --k) {
            var m = this._enums[k];
            var j = m[field];
            lookup[j] = m;
            if(j == value) {
                return m;
            }
        }
    }
    return null;
}

function defineEnum(definition) {
    var k;
    var e = new Enum();
    for(k in definition) {
        var j = definition[k];
        e[k] = j;
        e.addEnum(j)
    }
    return e;
}

你定义了这样的枚举:

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

一些缺点:

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

答案 11 :(得分:15)

ES7中,你可以依靠静态属性做一个优雅的ENUM:

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

然后

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

使用类而不是文字对象的优点是拥有父类Enum,然后您的所有枚举扩展该类。

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

答案 12 :(得分:12)

创建一个对象文字:

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

答案 13 :(得分:11)

如果您正在使用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()

答案 14 :(得分:8)

你的答案太复杂了

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

答案 15 :(得分:7)

我修改了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

答案 16 :(得分:6)

我提出了this方法,该方法以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
    }

})();

答案 17 :(得分:6)

IE8不支持freeze()方法 来源:http://kangax.github.io/compat-table/es5/,点击&#34;显示过时的浏览器?&#34;在顶部,并检查IE8&amp;冻结col col交叉口。

在我目前的游戏项目中,我使用了以下内容,因为很少有客户仍在使用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
}

答案 18 :(得分:5)

我对任何答案都不满意,所以我做了又一个枚举(YEA!)

此实现:

  • 使用更多最新的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

答案 19 :(得分:4)

我创建了一个可以在O(1)处获取值和名称的Enum类。它还可以生成包含所有名称和值的对象数组。

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选择选项:

var html = enum1.GetSelectOptionsHTML();

哪个成立:

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

答案 20 :(得分:4)

你可以做这样的事情

    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

答案 21 :(得分:4)

即使ES2015支持only static methods(而不是静态属性)(参见here,§15.2.2.2),奇怪的是你可以使用下面的Babel和{{1}预设:

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); 枚举),以及使用Webpack导入模块时。

此方法优于大多数其他答案的优势在于您可以将它与静态类型检查器一起使用(例如Flow)并且您可以在开发时使用静态类型检查进行断言,您的变量,参数等是特定的CellState“枚举”而不是其他枚举(如果您使用通用对象或符号,则无法区分)。

更新

上面的代码有一个缺点,即它允许创建CellState类型的其他对象(即使一个人不能将它们分配给CellState的静态字段,因为它已被冻结)。不过,以下更精确的代码具有以下优点:

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

    values

答案 22 :(得分:4)

我刚刚发布了一个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;

答案 23 :(得分:4)

var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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

答案 24 :(得分:4)

这就是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

答案 25 :(得分:3)

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

}

答案 26 :(得分:3)

这可能有用:

const [CATS, DOGS, BIRDS] = ENUM();

实现简单高效:

function * ENUM(count=1) { while(true) yield count++ }

生成器可以产生所需整数的精确序列,而无需知道有多少个常数。它还可以支持一个可选参数,该参数指定要从哪个(可能为负)数字开始(默认为1)。

答案 27 :(得分:3)

更新05.11.2020:
修改为包括静态字段和方法,以更接近地复制“真实”枚举行为。

有人尝试过使用包含私有字段和“获取”访问器的类来这样做吗? 我意识到私有类字段目前仍处于试验阶段,但它似乎可以用于创建具有不变字段/属性的类。浏览器支持也不错。唯一不支持它的“主要”浏览器是Firefox(我相信它们会很快推出)和IE(谁在乎)。

免责声明
我不是开发人员。我只是在寻找这个问题的答案,并开始考虑如何有时通过创建带有私有字段和受限属性访问器的类在C#中创建“增强型”枚举。

样本类别

class Sizes {
    // Private Fields
    static #_SMALL = 0;
    static #_MEDIUM = 1;
    static #_LARGE = 2;

    // Accessors for "get" functions only (no "set" functions)
    static get SMALL() { return this.#_SMALL; }
    static get MEDIUM() { return this.#_MEDIUM; }
    static get LARGE() { return this.#_LARGE; }
}

您现在应该可以直接调用枚举了。

Sizes.SMALL; // 0
Sizes.MEDIUM; // 1
Sizes.LARGE; // 2

结合使用私有字段和有限的访问器,意味着枚举值得到了很好的保护。

Sizes.SMALL = 10 // Sizes.SMALL is still 0
Sizes._SMALL = 10 // Sizes.SMALL is still 0
Sizes.#_SMALL = 10 // Sizes.SMALL is still 0

答案 28 :(得分:3)

最简单的解决方案:

创建

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

答案 29 :(得分:3)

我写了 enumerationjs 一个very tiny library to address the issue 确保类型安全,允许枚举常量从继承原型,保证枚举常量和枚举类型是不可变的+许多小功能。它允许重构大量代码并在枚举定义中移动一些逻辑。这是一个例子:

var CloseEventCodes = new Enumeration("closeEventCodes", {
  CLOSE_NORMAL:          { _id: 1000, info: "Connection closed normally" },
  CLOSE_GOING_AWAY:      { _id: 1001, info: "Connection closed going away" },
  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: "Connection closed due to protocol error"  },
  CLOSE_UNSUPPORTED:     { _id: 1003, info: "Connection closed due to unsupported operation" },
  CLOSE_NO_STATUS:       { _id: 1005, info: "Connection closed with no status" },
  CLOSE_ABNORMAL:        { _id: 1006, info: "Connection closed abnormally" },
  CLOSE_TOO_LARGE:       { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
    console.log(this.info); 
  }
});


CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true

Enumeration基本上是一个工厂。

Fully documented guide available here.希望这会有所帮助。

答案 30 :(得分:3)

以下是实现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

答案 31 :(得分:3)

截至撰写时, 2014年10月 - 所以这是一个现代的解决方案。我正在将解决方案编写为节点模块,并使用Mocha和Chai以及underscoreJS进行了测试。您可以轻松忽略这些,如果愿意,只需使用Enum代码。

看到很多帖子都有过于复杂的库等。在Javascript中获得枚举支持的解决方案非常简单,实际上并不需要。这是代码:

文件: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;

一个测试来说明它给你的东西:

file: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的模块化方法导出生成的Enums。

再一次,如果您只是一个临时用户,或者在浏览器等中,只需取出代码的工厂部分,如果您不想在代码中使用它,也可能删除下划线库。< / p>

答案 32 :(得分:3)

一种快速而简单的方法是:

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

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

答案 33 :(得分:2)

你可以试试这个:

C:\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\DataFactory Tools for Visual Studio\MsBuild\1.0

答案 34 :(得分:2)

我之前使用__defineGetter____defineSetter__defineProperty的混合物完成了它,具体取决于JS版本。

这是我制作的枚举生成函数:https://gist.github.com/gfarrell/6716853

你会这样使用它:

var Colours = Enum('RED', 'GREEN', 'BLUE');

它会创建一个不可变的字符串:int dictionary(enum)。

答案 35 :(得分:1)

Alien解决方案是让事情尽可能简单:

  1. 使用enum关键字(在javascript中保留)
  2. 如果enum关键字刚刚保留但未在您的javascript中实现,请定义以下内容

    const enumerate = spec => spec.split(/\s*,\s*/)
      .reduce((e, n) => Object.assign(e,{[n]:n}), {}) 
    
  3. 现在,您可以轻松使用它

    const kwords = enumerate("begin,end, procedure,if")
    console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)
    

    我认为没有理由让枚举值显式变量。无论如何,这些脚本都是形态的,如果您的代码的一部分是字符串或有效代码,则没有区别。真正重要的是,无论何时使用或定义它们,您都不需要处理大量的引号。

答案 36 :(得分:1)

我的观点中的枚举是什么:它是一个永远可访问的不可变对象,您可以相互比较项目,但项目具有共同的属性/方法,但对象他们自己或价值不能改变,他们只被实例化一次。

枚举用于比较数据类型,设置,采取/回复此类事件的操作。

因此,您需要具有相同实例的对象,以便检查它是否为枚举类型if(something instanceof enum) 此外,如果你得到一个enum对象,你希望能够用它做任何事情,无论枚举类型如何,它应该总是以相同的方式响应。

在我的例子中,它比较数据类型的值,但它可以是任何东西,从在3d游戏中修改面向方向的块到将值传递给特定的对象类型注册表。

请记住它是javascript并且没有提供固定的枚举类型,你最终总是制作自己的实现,因为这个线程显示有大量的实现,而没有一个是绝对正确的。


这是我用于枚举的内容。因为枚举是不可变的(或者至少应该是heh),所以我会冻结对象,以便它们不被轻易操作。

枚举可以由EnumField.STRING使用,它们有自己的方法可以使用它们的类型。 要测试某些内容是否传递给对象,您可以使用if(somevar instanceof EnumFieldSegment)

它可能不是最优雅的解决方案,我可以进行改进,但这种不可变的枚举(除非你解冻它)正是我需要的用例。

我也意识到我可以用{}覆盖原型,但我的思维能更好地用这种格式;-)射击我。

/**
 * simple parameter object instantiator
 * @param name
 * @param value
 * @returns
 */
function p(name,value) {
    this.name = name;
    this.value = value;
    return Object.freeze(this);
}
/**
 * EnumFieldSegmentBase
 */
function EnumFieldSegmentBase() {
    this.fieldType = "STRING";
}
function dummyregex() {
}
dummyregex.prototype.test = function(str) {
    if(this.fieldType === "STRING") {
        maxlength = arguments[1];
        return str.length <= maxlength;
    }
    return true;
};

dummyregexposer = new dummyregex();
EnumFieldSegmentBase.prototype.getInputRegex = function() { 
    switch(this.fieldType) {
        case "STRING" :     return dummyregexposer;  
        case "INT":         return /^(\d+)?$/;
        case "DECIMAL2":    return /^\d+(\.\d{1,2}|\d+|\.)?$/;
        case "DECIMAL8":    return /^\d+(\.\d{1,8}|\d+|\.)?$/;
        // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
        case "BOOLEAN":     return dummyregexposer;
    }
};
EnumFieldSegmentBase.prototype.convertToType = function($input) {
    var val = $input;
    switch(this.fieldType) {
        case "STRING" :         val = $input;break;
        case "INT":         val==""? val=0 :val = parseInt($input);break;
        case "DECIMAL2":    if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break;
        case "DECIMAL8":    if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break;
        // boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
        case "BOOLEAN":     val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf()))  ;break;
    }
    return val;
};
EnumFieldSegmentBase.prototype.convertToString = function($input) {
    var val = $input;
    switch(this.fieldType) {
        case "STRING":      val = $input;break;
        case "INT":         val = $input+"";break;
        case "DECIMAL2":    val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break;
        case "DECIMAL8":    val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break;
        case "BOOLEAN":     val = $input ? "true" : "false"  ;break;
    }
    return val;
};
EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) {
    var val = false;
    switch(this.fieldType) {
        case "STRING":      val = ($val1===$val2);break;
        case "INT":         val = ($val1===$val2);break;
        case "DECIMAL2":    val = ($val1.comparedTo($val2)===0);break;
        case "DECIMAL8":    val = ($val1.comparedTo($val2)===0);break;
        case "BOOLEAN":     val = ($val1===$val2);break;
    }
    return val;
};

/**
 * EnumFieldSegment is an individual segment in the 
 * EnumField
 * @param $array An array consisting of object p
 */
function EnumFieldSegment() {
    for(c=0;c<arguments.length;c++) {
        if(arguments[c] instanceof p) {
            this[arguments[c].name] = arguments[c].value;
        }
    }
    return Object.freeze(this); 
}
EnumFieldSegment.prototype = new EnumFieldSegmentBase();
EnumFieldSegment.prototype.constructor = EnumFieldSegment;


/**
 * Simple enum to show what type of variable a Field type is.
 * @param STRING
 * @param INT
 * @param DECIMAL2
 * @param DECIMAL8
 * @param BOOLEAN
 * 
 */
EnumField = Object.freeze({STRING:      new EnumFieldSegment(new p("fieldType","STRING")), 
                            INT:        new EnumFieldSegment(new p("fieldType","INT")), 
                            DECIMAL2:   new EnumFieldSegment(new p("fieldType","DECIMAL2")), 
                            DECIMAL8:   new EnumFieldSegment(new p("fieldType","DECIMAL8")), 
                            BOOLEAN:    new EnumFieldSegment(new p("fieldType","BOOLEAN"))});

答案 37 :(得分:1)

基本上有两种枚举类型,全局枚举(如C)和类对象(如TypeScript)。对于全局枚举,请执行以下操作-

// Note that // enum is optional, though it makes it look slightly better.
const // enum
  SUNDAY = 1,
  MONDAY = 2,
  TUESDAY = 3,
  WEDNSDAY = 4,
  THURSDAY = 5,
  FRIDAY = 6,
  SATURDAY = 7;

对于类对象的枚举,请执行此操作(例如Artur Czajka的答案)-

// A trailing comma isn't required but is a good habit.
const Days = Object.freeze({
  SUNDAY = 1,
  MONDAY = 2,
  TUESDAY = 3,
  WEDSNDAY = 4,
  THURSDAY = 5,
  FRIDAY = 6,
  SATURDAY = 7,
});

const Days = {
  SUNDAY = 1,
  MONDAY = 2,
  TUESDAY = 3,
  WEDSNDAY = 4,
  THURSDAY = 5,
  FRIDAY = 6,
  SATURDAY = 7,
};
Object.freeze(Days);

第一个声明类对象枚举的方法看起来更简洁。顺便说一句,全局和类对象枚举并不是真正正确的术语,我对此做了补充。

编辑:

由Aral Roca制作(针对全局枚举)的解决方案,它看起来很棒,但有一个缺点(例如缓慢0.1秒)-

function* ENUM(count = 1) {
  while (true) yield count++;
}

然后

const [ RED, GREEN, BLUE ] = ENUM();

答案 38 :(得分:1)

您可以使用Object.prototype.hasOwnProperty()

var findInEnum,
    colorEnum = {
    red : 0,
    green : 1,
    blue : 2
};

// later on

findInEnum = function (enumKey) {
  if (colorEnum.hasOwnProperty(enumKey)) {
    return enumKey+' Value: ' + colorEnum[enumKey]
  }
}

alert(findInEnum("blue"))

答案 39 :(得分:1)

真的很像@Duncan上面所做的,但我不喜欢用Enum来破坏全局Object函数空间,所以我写了以下内容:

function mkenum_1()
{
  var o = new Object();
  var c = -1;
  var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};

  for (i in arguments) {
    var e = arguments[i];
    if ((!!e) & (e.constructor == Object))
      for (j in e)
        f(j, (c=e[j]));
    else
      f(e, ++c);
    }

  return Object.freeze ? Object.freeze(o) : o;
}

var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');

console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);

// Output is:
// MED := 1
// LRG := 100
@Stijin也有一个简洁的解决方案(指他的博客),其中包括这些对象的属性。我也为此编写了一些代码,我接下来要包含这些代码。

function mkenum_2(seed)
{
    var p = {};

    console.log("Seed := " + seed);

    for (k in seed) {
        var v = seed[k];

        if (v instanceof Array)
            p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
        else
            p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
    }
    seed.properties = p;

    return Object.freeze ? Object.freeze(seed) : seed;
}

此版本生成一个额外的属性列表,允许友好的名称转换和短代码。我喜欢这个版本,因为不需要在属性中复制数据条目,因为代码会为你完成。

var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });

这两个可以合并为一个处理单元,mkenum,(使用枚举,分配值,创建和添加属性列表)。然而,由于我今天已经花了太多时间在这上面,我将把这个组合作为一个练习给亲爱的读者。

答案 40 :(得分:1)

class Enum {
  constructor (...vals) {
    vals.forEach( val => {
      const CONSTANT = Symbol(val);
      Object.defineProperty(this, val.toUpperCase(), {
        get () {
          return CONSTANT;
        },
        set (val) {
          const enum_val = "CONSTANT";
          // generate TypeError associated with attempting to change the value of a constant
          enum_val = val;
        }
      });
    });
  }
}

用法示例:

const COLORS = new Enum("red", "blue", "green");

答案 41 :(得分:1)

自ES6以来,您无法本身创建枚举,但可以使用一种更优雅的语法来实现代码自动补全,至少在VSCode上:

class MyEnum {
  const A = '1'
  const B = '2'
  const C = '3'
}

从好的方面来说,您可以将所需的任何内容放入const内,就像其他回复一样。另外,在Node中,您可以将其导出为模块的一部分,同时保留有意义的名称。

import { MyEnum } from './my-enum'

console.log(MyEnum.B)

希望这会有所帮助。

答案 42 :(得分:1)

阅读所有答案,未找到任何非冗长且干燥的解决方案。 我使用这种单线:

const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});

它生成具有人类可读值的对象:

{
  DRAW: 'DRAW',
  SCALE: 'SCALE',
  DRAG: 'DRAG'
}

答案 43 :(得分:0)

注意:此答案已过时。你应该看到我的updated answer

我迟到了,但是在 C/Java 中,枚举不是带有数值的字典,而是类。我试图模仿这一点:

function Enum(vals) {
  vals = vals.map((el) => ""+el);
  function T(value) {
    value = ""+value;
    if(vals.indexOf(value) === -1) {
         throw new Error("invalid enum value");
    }
    
    this.value = value;
  }
   

  T.prototype.toString = function() { return this.value; }
  T.values = function () { return vals; }

  for(var i = 0; i < vals.length; i++) {
    T[vals[i]] = new T(vals[i]);
  } 

  Object.freeze(T);

  return T;
 }

//Important: DO NOT use "new"!
var Colors = Enum(["RED","GREEN","BLUE"]);

//Changing a static property no effect...
Colors.RED = "something";

//...But you can still add to prototype
Colors.prototype.something = 100;
console.log(Colors.RED);

答案 44 :(得分:0)

这是我对(标记的)Enum工厂的看法。这是working demo

/*
 * Notes: 
 * The proxy handler enables case insensitive property queries
 * BigInt is used to enable bitflag strings /w length > 52
*/
function EnumFactory() {
  const proxyfy = {
    construct(target, args) { 
      const caseInsensitiveHandler = { 
          get(target, key) {
          return target[key.toUpperCase()] || target[key];  
        } 
      };
      const proxified = new Proxy(new target(...args), caseInsensitiveHandler ); 
      return Object.freeze(proxified);
    },
  }
  const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);
  const throwIf = (
      assertion = false, 
      message = `Unspecified error`, 
      ErrorType = Error ) => 
      assertion && (() => { throw new ErrorType(message); })();
  const hasFlag = (val, sub) => {
    throwIf(!val || !sub, "valueIn: missing parameters", RangeError);
    const andVal = (sub & val);
    return andVal !== BigInt(0) && andVal === val;
  };

  function EnumCtor(values) {
    throwIf(values.constructor !== Array || 
            values.length < 2 || 
        values.filter( v => v.constructor !== String ).length > 0,
      `EnumFactory: expected Array of at least 2 strings`, TypeError);
    const base = BigInt(1);
    this.NONE = BigInt(0);
    values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );
  }

  EnumCtor.prototype = {
    get keys() { return Object.keys(this).slice(1); },
    subset(sub) {
      const arrayValues = this.keys;
      return new ProxiedEnumCtor(
        [...sub.toString(2)].reverse()
          .reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )
      );
    },
    getLabel(enumValue) {
      const tryLabel = Object.entries(this).find( value => value[1] === enumValue );
      return !enumValue || !tryLabel.length ? 
        "getLabel: no value parameter or value not in enum" :
        tryLabel.shift();
    },
    hasFlag(val, sub = this) { return hasFlag(val, sub); },
  };
  
  return arr => new ProxiedEnumCtor(arr);
}

答案 45 :(得分:0)

com.recoyxgroup.javascript.enum包允许您正确定义枚举并标记枚举类,其中:

  • 每个常量都表示为一个不变的{ _value: someNumber }
  • 每个常量都有一个附加到枚举类(E.CONSTANT_NAME)的属性
  • 每个常量都有一个友好的字符串(constantName
  • 每个常量都有一个数字(someNumber
  • 您可以使用E.prototype声明自定义属性/方法。

在需要特定枚举的地方,约定是E(v),如下所示:

const { FlagsEnum } from 'com.recoyxgroup.javascript.enum';

const Rights = FlagsEnum('Rights', [
    'ADMINISTRATION',
    'REVIEW',
]);

function fn(rights) {
    rights = Rights(rights);
    console.log('administration' in rights, 'review' in rights);
}

fn( ['administration', 'review'] ); // true true
fn( 'administration' ); // true false
fn( undefined ); // false false

var r = Rights.ADMINISTRATION;
console.log( r == 'administration' );

如您所见,您仍然可以将值与字符串进行比较。

定义可以更具体:

const E = FlagsEnum('E', [
    ['Q', 0x10],
    ['K', 'someB'],
    ['L', [0x40, 'someL']],
]);

FlagsEnum产品>实例属性/方法

  • 数字(原为valueOf(),但由于JS ==,必须为“数字”)
  • set()
  • exclude()
  • toggle()
  • filter()
  • valueOf()
  • toString()

答案 46 :(得分:0)

此答案是针对特定情况的替代方法。我需要一组基于属性子值的位掩码常量(属性值是数组或值列表的情况)。它涵盖了多个重叠枚举的等效项。

我创建了一个用于存储和生成位掩码值的类。然后,我可以通过这种方式使用伪常量位掩码值进行测试,例如,如果RGB值中存在绿色:

if (value & Ez.G) {...}

在我的代码中,我仅创建此类的一个实例。如果没有实例化该类的至少一个实例,似乎没有一种干净的方法可以做到这一点。这是类声明和位掩码值生成代码:

class Ez {
constructor() {
    let rgba = ["R", "G", "B", "A"];
    let rgbm = rgba.slice();
    rgbm.push("M");              // for feColorMatrix values attribute
    this.createValues(rgba);
    this.createValues(["H", "S", "L"]);
    this.createValues([rgba, rgbm]);
    this.createValues([attX, attY, attW, attH]);
}
createValues(a) {                // a for array
    let i, j;
    if (isA(a[0])) {             // max 2 dimensions
        let k = 1;
        for (i of a[0]) {
            for (j of a[1]) {
                this[i + j] = k;
                k *= 2;
            }
        }
    }
    else {                       // 1D array is simple loop
        for (i = 0, j = 1; i < a.length; i++, j *= 2)
            this[a[i]] = j;
   }
}

2D数组用于SVG feColorMatrix values属性,它是RGBA x RGBAM的4x5矩阵,其中M是一个乘数。所得的Ez属性为Ez.RR,Ez.RG等。

答案 47 :(得分:0)

你可以使用一个简单的函数来反转键和值,它也可以用于数组,因为它将数字整数字符串转换为数字。对于此用例和其他用例,代码很小,简单且可重用。

&#13;
&#13;
var objInvert = function (obj) {
    var invert = {}
    for (var i in obj) {
      if (i.match(/^\d+$/)) i = parseInt(i,10)
      invert[obj[i]] = i
    }
    return invert
}
 
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL',
'BOSSA-NOVA','POP','INDIE']))

console.log(musicStyles)
&#13;
&#13;
&#13;

答案 48 :(得分:0)

var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });

您不需要指定 id ,您只需使用空对象来比较枚举。

if (incommingEnum === DaysEnum.monday) //incommingEnum is monday

编辑:如果要序列化对象(例如JSON),则再次 id

答案 49 :(得分:-1)

export const ButtonType = Object.freeze({ 
   DEFAULT: 'default', 
   BIG: 'big', 
   SMALL: 'small'
})

来源:https://medium.com/@idanlevi2/enum-in-javascript-5f2ff500f149

答案 50 :(得分:-1)

你也可以尝试定义一个新的函数,然后创建一个新的命名空间,然后向它添加变量,就像这样。

function Color () {};  
Color.RED = 1;
Color.YELLOW = 2;

只要有人使用Color函数授予的命名空间,一切都会好起来的。 如果您了解Java,这是一种旧的枚举:我们只使用类或接口来保存静态属性。如果一个函数,在javascript中,是一种类,这几乎是相同的方法。

我这是一种定义枚举的简单方法。

希望它有所帮助!

问候。

胜者。

答案 51 :(得分:-1)

您可以尝试使用https://bitbucket.org/snippets/frostbane/aAjxM

my.namespace.ColorEnum = new Enum(
    "RED = 0",
    "GREEN",
    "BLUE"
)

它应该工作到ie8。

相关问题