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

What is the preferred syntax for defining enums in JavaScript?

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

1
2
3
4
5
6
7
8
9
10
11
my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

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

还是有更好的成语?


Since 1.8.5 it's possible to seal and freeze the object, so define the above as:

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

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

瞧!JS枚举。

注:我是在2011年写的,但现在是2019年——使用const来防止重写枚举字典。

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

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

确保更大程度的类型安全性(使用枚举或其他方式)的一种方法是使用类似于typescript或flow的工具。

来源

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


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

尽管如此,由于值是什么并不重要(您已经使用了0、1、2),我将使用一个有意义的字符串,以防您想要输出当前值。


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

已经可以警告该名称:

1
2
3
4
5
6
7
8
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);
   }
}

或者,您可以制作值对象,这样您就可以吃蛋糕了:

1
2
3
4
5
var SIZE = {
  SMALL : {value: 0, name:"Small", code:"S
<div class="
suo-content">[collapse title=""]<ul><li>那么,如果你只有一个尺寸的名字,你会怎么创建它呢?</li><li>@Johanisma:对于枚举来说,这个用例并不真正有意义,因为它的整体概念是您提前知道所有的值。但是,没有什么能阻止您在以后的JavaScript中添加额外的值。我将在我的答案中添加一个这样的例子。</li><li>+1用于链接到您的文章的属性方法。优雅之处在于,基本的声明很简单,就像在OP中一样,在需要时具有附加的属性特性。</li><li>@斯蒂金,真的很喜欢你最新的解决方案。在您的博客的评论和下面的评论中发布了代码。基本上,使用一个函数,从一个现有的散列列表中执行属性构建,并有选择地冻结它(mkenum_2在我的列表中)。干杯。</li><li>还有一个实现它的库,还包括比较和反向搜索的好特性:github.com/adrai/enum</li><li>在您的博客文章中,您提到序列化是您不再喜欢这种枚举方式的原因之一,但是这个问题可以通过添加tojson()方法来解决。是的,这是创建枚举的另一个步骤,但您可以将其作为mixin来完成。</li><li>@Kevinwiskia是的,一个<wyn>toJSON</wyn>方法可以让你走到一半。但反序列化还需要一个<wyn>reviver</wyn>…而那个复兴者必须被移交给江户十一〔二〕…所以你不能只添加一个<wyn>fromJSON</wyn>或其他东西就可以完成。您需要注意,每当JSON可能包含您喜欢的枚举时,都会使用这个reviver函数…所以这确实让事情变得更复杂了。</li></ul>[/collapse]</div><hr><P>底线:你不能。</P><P>你可以伪造它,但你不会得到类型安全。通常,这是通过创建一个简单的字符串值字典映射到整数值来完成的。例如:</P>[cc lang="javascript"]var DaysEnum = {"monday":1,"tuesday":2,"wednesday":3, ...}

Document.Write("
Enumerant:" + DaysEnum.tuesday);

这种方法的问题是什么?您可以意外地重新定义枚举值,或者意外地具有重复的枚举值。例如:

1
DaysEnum.monday = 4; // whoops, monday is now thursday, too

编辑

What about Artur Czajka's Object.freeze? Wouldn't that work to prevent you from setting monday to thursday? – Fry Quad

当然,Object.freeze完全可以解决我抱怨的问题。我想提醒大家,当我写这篇文章时,Object.freeze并不真正存在。

现在。。。。现在它打开了一些非常有趣的可能性。

编辑2这里有一个创建枚举的非常好的库。

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

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


我们都想要:

1
2
3
4
5
function Enum(constantsList) {
    for (var i in constantsList) {
        this[constantsList[i]] = i;
    }
}

现在可以创建枚举:

1
2
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添加方法:

1
2
3
4
5
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上不能正常工作…应该坚持以前的版本)

1
2
3
4
5
6
7
8
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()。例如:

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

要简化调试,可以向枚举值添加说明:

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

柱塞演示

在GitHub上,您可以找到一个包装器,它简化了初始化枚举所需的代码:

1
2
3
4
5
6
7
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


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

使用Object.defineProperty,我想我找到了一个可行的解决方案。

这是一个jsFiddle:http://jsFiddle.net/zv4a6a/

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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开始,每项递增。

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


上面已经列出了大多数人的"首选语法"。然而,有一个主要的首要问题:代码大小。这里列出的所有其他答案都会将您的代码大小放大到极致。为了获得最佳的性能、代码的可读性、大规模的项目管理以及通过缩小代码大小,这是进行枚举的正确方法。

1
2
3
4
5
6
7
8
9
10
const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;

// later on

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

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

(长度:2450字节)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
(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(rawstr) {
        rawstr = rawstr.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);

有人可能会说,这比其他解决方案更不实用:它占用了大量的空间,需要很长的时间来编写,而且没有覆盖糖语法。如果这些人不缩小他们的代码,他们是对的。然而,没有一个合理的人会在最终产品中留下未完成的代码。对于这个小型化,闭包编译器是我所能找到的最好的工具。在线访问可以在这里找到。闭包编译器能够获取所有这些枚举数据并将其内联,使您的javascript非常小,运行速度非常快。观察。

(长度:605字节)

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

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

不使用枚举的源(长度:1973字节(比枚举代码短477字节!))< BR>未使用枚举进行缩小(长度:843字节(比枚举代码长238字节))

如图所示,在没有枚举的情况下,源代码更短,而代价是更大的小型代码。我不了解您,我确信我不喜欢将源代码合并到最终产品中,这使得这个枚举非常优越,因为它会导致较小的文件大小。

这种枚举形式的另一个优点是,它可以用来轻松地管理大型项目,而不牺牲最小的代码大小。当与许多其他人一起处理一个大型项目时,明确地标记和标记变量名以及谁创建了代码可能会有好处,这样代码的原始创建者就可以被快速识别出来进行协作性的错误修复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 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(
  currentColor === ENUM_PL_ARRAYTYPE_UNSORTED ||
  currentColor === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

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

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


这是我知道的一个老版本,但从那时起通过typescript接口实现它的方式是:

1
2
3
4
5
6
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],而不管声明的顺序如何。


使用JavaScript代理

tldr:将这个类添加到实用程序方法中,并在代码中使用它,它模拟传统编程语言的枚举行为,并在尝试访问不存在的枚举器或添加/更新枚举器时实际抛出错误。无需依赖Object.freeze()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
  }
}

然后通过实例化类来创建枚举:

1
2
3
4
const roles = new Enum({
  ADMIN: 'Admin',
  USER: 'User',
});

完整解释:

从传统语言中获得的枚举的一个非常有益的特性是,如果您试图访问一个不存在的枚举器,它们就会爆炸(引发编译时错误)。

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

正如您可能知道的那样,访问javascript中不存在的成员只会返回undefined,而不会破坏代码。由于枚举器是预先定义的常量(即一周中的几天),所以不应该出现枚举器未定义的情况。

别误会,javascript在访问未定义的属性时返回undefined的行为实际上是一种非常强大的语言特性,但是当你试图模仿传统的枚举结构时,它不是你想要的特性。

这就是代理对象发光的地方。通过引入ES6(ES2015),代理在语言上实现了标准化。以下是MDN的描述:

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function
invocation, etc).

与Web服务器代理类似,JavaScript代理能够截获对象上的操作(使用"陷阱",如果您愿意,可以调用它们挂钩),并允许您在完成之前执行各种检查、操作和/或操作(或者在某些情况下,完全停止操作,这正是我们在尝试和尝试o引用一个不存在的枚举器)。

下面是一个使用代理对象模拟枚举的人为示例。本例中的枚举器是标准HTTP方法(即"get"、"post"等):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 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"

旁白:什么是代理?

我记得当我第一次看到"代理"这个词时,它在很长一段时间内对我来说毫无意义。如果现在是你,我认为归纳代理的一个简单方法就是把它们看作是软件、机构,甚至是在两个服务器、公司或人员之间充当中间人或中间人的人。


这是我使用的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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;
}

你这样定义你的枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

这就是访问枚举的方式:

1
2
3
4
5
6
7
8
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
});

我通常使用最后2个方法来映射来自消息对象的枚举。

这种方法的一些优点:

  • 易于声明的枚举
  • 易于访问您的枚举
  • 枚举可以是复杂类型
  • 如果经常使用getByValue,则枚举类具有一些关联缓存。

一些缺点:

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

在ES7中,可以根据静态属性执行优雅的枚举:

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

然后

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

(使用类而不是文本对象)的优点是有一个父类Enum,那么所有枚举都将扩展该类。

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


创建对象文本:

1
2
3
4
5
const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};


如果您使用的是主干,那么可以使用主干.collection免费获得完整的枚举功能(按ID、名称、自定义成员查找)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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()


你的答案太复杂了

1
2
3
4
5
6
7
8
9
10
11
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


我修改了安德烈"fi"的解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  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);
    }
  }

测试:

1
2
3
4
5
6
7
8
9
10
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

IE8不支持freeze()方法。来源:http://kangax.github.io/compat-table/es5/,单击"显示过时的浏览器?"在顶部,检查IE8和冻结行列交叉点。

在我当前的游戏项目中,我使用了以下内容,因为很少有客户仍然使用IE8:

1
2
3
4
5
6
var CONST_WILD_TYPES = {
    REGULAR: 'REGULAR',
    EXPANDING: 'EXPANDING',
    STICKY: 'STICKY',
    SHIFTING: 'SHIFTING'
};

我们也可以这样做:

1
2
3
4
5
6
var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

甚至这个:

1
2
3
4
5
6
var CONST_WILD_TYPES = {
    REGULAR: '1',
    EXPANDING: '2',
    STICKY: '3',
    SHIFTING: '4'
};

最后一个,对于字符串来说似乎是最有效的,如果您有服务器和客户机交换这些数据,它会减少您的总带宽。当然,现在您有责任确保数据中没有冲突(re、ex等必须是唯一的,1、2等也应该是唯一的)。请注意,为了向后兼容性,您需要永远维护它们。

任务:

1
var wildType = CONST_WILD_TYPES.REGULAR;

比较:

1
2
3
if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}

我提出了这种方法,它是用Java中的枚举建模的。这些是类型安全的,因此您也可以执行instanceof检查。

可以这样定义枚举:

1
var Days = Enum.define("Days", ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]);

Days现在指Days枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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"

实施情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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 +".");" +
               
<div class="suo-content">[collapse title=""]<ul><li>看起来不错,也许您应该检查一下<wyn>freeze</wyn>方法是否存在向后兼容性?例如,<wyn>if (Object.freeze) { Object.freeze(values); }</wyn></li><li>好点!会的!</li></ul>[/collapse]</div><hr><P>这就是typescript如何将它的<wyn>enum</wyn>转换为javascript:</P>[cc lang="javascript"]var makeEnum = function(obj) {
    obj[ obj['Active'] = 1 ] = 'Active';
    obj[ obj['Closed'] = 2 ] = 'Closed';
    obj[ obj['Deleted'] = 3 ] = 'Deleted';
}

现在:

1
2
makeEnum( NewObj = {} )
// => {1:"Active", 2:"Closed", 3:"Deleted", Active: 1, Closed: 2, Deleted: 3}

起初,我很困惑为什么obj[1]返回'Active',但后来意识到它死了的简单赋值运算符赋值,然后返回它:

1
2
obj['foo'] = 1
// => 1

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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);

你可以这样初始化:

1
2
3
4
5
var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

要获取值(如c中的枚举):

1
var val2 = enum1.item2;

要获取值的名称(在为不同的名称放置相同的值时可能不明确):

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

要获取对象中每个名称和值的数组,请执行以下操作:

1
var arr = enum1.GetObjArr();

将产生:

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

您还可以轻松获得HTML选择选项:

1
var html = enum1.GetSelectOptionsHTML();

持有:

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

尽管ES2015中只支持静态方法(而不是静态属性),但奇怪的是,您可以将下面的内容与带es2015预设的babel一起使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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导入模块时,它也能正常工作。

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

更新

上面的代码有一个缺陷,即它允许创建CellState类型的额外对象(即使不能将它们分配给CellState的静态字段,因为它被冻结)。不过,下面更精细的代码提供了以下优势:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    '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;

  • 你可以这样做

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
        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
    <hr><P>我刚刚发布了一个NPM包gen_enum,允许您快速在javascript中创建枚举数据结构:</P>[cc lang="
    javascript"]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+浏览器),返回的枚举对象是不可变的。

    有关详细信息,请访问https://github.com/greenlaw110/enumjs

    更新

    我废弃了gen_enum包并将函数合并到constjs包中,该包提供了更多的功能,包括不可变对象、JSON字符串反序列化、字符串常量和位图生成等。有关详细信息,请访问https://www.npmjs.com/package/constjs。

    gen_enum升级到constjs只需更改语句

    1
    var genEnum = require('gen_enum');

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

    1
    2
    3
    4
    5
    var ColorEnum = {
        red: {},
        green: {},
        blue: {}
    }

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


    Simplest solution:

    Create

    1
    2
    3
    4
    5
    6
    var Status = Object.freeze({
       "Connecting":0,
       "Ready":1,
       "Loading":2,
       "Processing": 3
    });

    获取价值

    1
    console.log(Status.Ready) // 1

    获取密钥

    1
    console.log(Object.keys(Status)[Status.Ready]) // Ready

    ES7方式,(迭代器,冻结),用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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'

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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(', ') }

    }

    一个快速简单的方法是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var Colors = function(){
    return {
        'WHITE':0,
        'BLACK':1,
        'RED':2,
        'GREEN':3
        }
    }();

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


    截至2014年10月,这是一个当代的解决方案。我将该解决方案编写为一个节点模块,并包含了一个使用mocha和chai以及下划线的测试。您可以很容易地忽略这些,如果愿意,只需使用枚举代码。

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

    文件:枚举

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    _ = 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;

    一个测试来说明它给了你什么:

    文件:enumspec.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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.keys来获取所有的键,并且您可以将键本身与整型常量匹配。您可以使用不同的值重用工厂,并使用节点的模块化方法导出生成的枚举。

    同样,如果您只是一个临时用户,或者在浏览器等中,只需将代码作为工厂代码的一部分,如果您不想在代码中使用下划线库,也可能删除下划线库。


    I wrote enumerationjs a very tiny library to address the issue which ensures type safety, allow enum constants to inherit from a prototype, guaranties enum constants and enum types to be immutable + many little features. It allows to refactor a lot of code and move some logic inside the enum definition. Here is an example :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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基本上是一个工厂。

    这里提供完整的文档指南。希望这有帮助。


    下面是实现typescript枚举的几种不同方法。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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等价物,因此如果需要,您可以轻松地将它们切换出去。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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


    我想这很容易用。https://stackoverflow.com/a/32245370/4365315

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    var Helper = {
        isEmpty: function (obj) {
            return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
        },

        isObject: function (obj) {
            return (typeof obj === 'object');
        },

        sortObjectKeys: function (object) {
            return Object.keys(object)
                .sort(function (a, b) {
                    c = a - b;
                    return c
                });
        },
        containsItem: function (arr, item) {
            if (arr && Array.isArray(arr)) {
                return arr.indexOf(item) > -1;
            } else {
                return arr === item;
            }
        },

        pushArray: function (arr1, arr2) {
            if (arr1 && arr2 && Array.isArray(arr1)) {
                arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
            }
        }
    };
    function TypeHelper() {
        var _types = arguments[0],
            _defTypeIndex = 0,
            _currentType,
            _value,
            _allKeys = Helper.sortObjectKeys(_types);

        if (arguments.length == 2) {
            _defTypeIndex = arguments[1];
        }

        Object.defineProperties(this, {
            Key: {
                get: function () {
                    return _currentType;
                },
                set: function (val) {
                    _currentType.setType(val, true);
                },
                enumerable: true
            },
            Value: {
                get: function () {
                    return _types[_currentType];
                },
                set: function (val) {
                    _value.setType(val, false);
                },
                enumerable: true
            }
        });
        this.getAsList = function (keys) {
            var list = [];
            _allKeys.forEach(function (key, idx, array) {
                if (key && _types[key]) {

                    if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                        var json = {};
                        json.Key = key;
                        json.Value = _types[key];
                        Helper.pushArray(list, json);
                    }
                }
            });
            return list;
        };

        this.setType = function (value, isKey) {
            if (!Helper.isEmpty(value)) {
                Object.keys(_types).forEach(function (key, idx, array) {
                    if (Helper.isObject(value)) {
                        if (value && value.Key == key) {
                            _currentType = key;
                        }
                    } else if (isKey) {
                        if (value && value.toString() == key.toString()) {
                            _currentType = key;
                        }
                    } else if (value && value.toString() == _types[key]) {
                        _currentType = key;
                    }
                });
            } else {
                this.setDefaultType();
            }
            return isKey ? _types[_currentType] : _currentType;
        };

        this.setTypeByIndex = function (index) {
            for (var i = 0; i < _allKeys.length; i++) {
                if (index === i) {
                    _currentType = _allKeys[index];
                    break;
                }
            }
        };

        this.setDefaultType = function () {
            this.setTypeByIndex(_defTypeIndex);
        };

        this.setDefaultType();
    }

    var TypeA = {
       "-1":"Any",
       "2":"2L",
       "100":"100L",
       "200":"200L",
       "1000":"1000L"
    };

    var enumA = new TypeHelper(TypeA, 4);

    document.writeln("Key =", enumA.Key,", Value =", enumA.Value,"");


    enumA.setType("200L", false);
    document.writeln("Key =", enumA.Key,", Value =", enumA.Value,"");

    enumA.setDefaultType();
    document.writeln("Key =", enumA.Key,", Value =", enumA.Value,"");


    enumA.setTypeByIndex(1);
    document.writeln("Key =", enumA.Key,", Value =", enumA.Value,"");

    document.writeln("is equals =", (enumA.Value == TypeA["2"]));


    我以前用过__defineGetter____defineSetter__defineProperty的混合物,这取决于JS版本。

    下面是我创建的枚举生成函数:https://gist.github.com/gfarrell/6716853

    您可以这样使用它:

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

    它将创建一个不可变的字符串:int dictionary(枚举)。


    您可以尝试以下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       var Enum = Object.freeze({
                Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
                Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
                });

        alert(Enum.Role.Supervisor);
        alert(Enum.Color.GREEN);
        var currentColor=0;
        if(currentColor == Enum.Color.RED) {
           alert('Its Red');
        }

    外星人的解决方案是尽可能简单:

  • 使用枚举关键字(在javascript中保留)
  • 如果枚举关键字只是保留的,但没有在JavaScript中实现,请定义以下内容

    1
    2
    const enumerate = spec => spec.split(/\s*,\s*/)
      .reduce((e, n) => Object.assign(e,{[n]:n}), {})
  • 现在,你可以很容易地使用它

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

    我看不出有任何理由使枚举值显式化变量。不管怎样,脚本都是变形的,如果代码的一部分是字符串或有效代码,那就没有区别了。真正重要的是,无论何时使用或定义引号,都不需要处理成吨的引号。


    您可以使用object.prototype.hasownProperty()。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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"))


    由于ES6,您不能本身创建枚举,但可以使用更优雅的语法来自动完成代码,至少在vscode:

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

    另一方面,你可以把你想要的任何东西放在const中,就像其他回复一样。此外,在节点中,可以将其作为模块的一部分导出,同时保留有意义的名称。

    1
    2
    3
    import { MyEnum } from './my-enum'

    console.log(MyEnum.B)

    希望这有帮助。


    在我看来,枚举是什么:它是一个始终可访问的不可变对象,您可以将项与其他项进行比较,但这些项具有通用的属性/方法,但不能更改对象本身或值,并且它们只能实例化一次。

    枚举是imho用来比较数据类型、设置、采取/回复类似操作的操作。

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

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

    记住,它是javascript,不提供固定的枚举类型,您最终总是自己创建实现,正如这个线程所显示的那样,有大量的实现没有一个是绝对正确的。

    这是我用来做枚举的。因为枚举是不可变的(或者至少应该是heh),所以我冻结了对象,这样就不能轻松地操纵它们。

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

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

    我也意识到我可以用替代原型,但是我的大脑在这种格式下工作得更好;-)开枪。

    <预> <代码> /***简单参数对象实例化器*@ PARAM名称*@帕拉姆值*@返回*/函数P(名称、值){this.name=名称;this.value=值;返回对象。冻结(this);}/***EnumFieldSegmentBase(EnumFieldSegmentBase)*/函数EnumFieldSegmentBase()。{this.fieldtype="字符串";}函数dummoregex()。{}dummyregex.prototype.test=函数(str){if(this.fieldType=="字符串")。{maxlength=参数[1];返回str.length<=maxlength;}回归真实;};dummyregexposer=new dummyregex();EnumFieldSegmentBase.Prototype.GetInputRegEx=函数()。{开关(this.fieldtype){case"string":返回dummyregexposer;案例"int":返回/^(d+)?美元/大小写"decimal2":返回/^d+(.d 1,2 d+.)?美元/大小写"decimal8":返回/^d+(.d 1,8 d+.)?美元///布尔值很难处理,迪克。如果它是一个布尔值假,如果它是一个字符串,如果它是空的0,或者它是假,否则让我们看看布尔值产生了什么。case"boolean":返回dummoregexposer;}};EnumFieldSegmentBase.Prototype.ConvertToType=函数($input){var val=$输入;开关(this.fieldtype){case"string":val=$input;break;案例"int":val=""?val=0:val=parseint($input);中断;案例"小数2":如果($input==""$input==null)$input=0

    很像@duncan上面所做的,但我不喜欢用枚举来破坏全局对象函数空间,所以我写了以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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还有一个简洁的解决方案(参考他的博客),其中包括这些对象的属性。我也为此写了一些代码,包括next。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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;
    }

    此版本生成一个附加的属性列表,允许友好的名称转换和短代码。我喜欢这个版本,因为它不需要像代码那样在属性中复制数据条目。

    1
    2
    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,(使用枚举,分配值,创建和添加属性列表)。然而,由于我今天已经花了太多时间在这个问题上,我将把这个组合留给亲爱的读者作为练习。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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;
            }
          });
        });
      }
    }

    使用示例:

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


    这个答案是针对特定情况的另一种方法。我需要一组基于属性子值的位掩码常量(属性值是数组或值列表的情况)。它包含几个重叠的枚举的等价物。

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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;
       }
    }

    二维数组用于svg fecolormatrix values属性,它是rgba m对rgba的4x5矩阵,其中m是乘数。生成的EZ属性为EZ.RR、EZ.RG等。


    阅读所有答案,没有发现任何非冗长和干燥的解决方案。我用这一行:

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

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

    1
    2
    3
    4
    5
    {
      DRAW: 'DRAW',
      SCALE: 'SCALE',
      DRAG: 'DRAG'
    }


    您可以使用一个简单的函数来反转键和值,它还可以与数组一起工作,因为它可以将数字整型字符串转换为数字。对于这个和其他用例,代码是小的、简单的和可重用的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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)


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

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

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

    编辑:如果您要序列化对象(例如JSON),您将再次获得ID。

    • (摘自加布里埃尔·拉马斯的评论)
    • (根据Stijn de Witt的评论进行编辑)


    您还可以尝试在新的名称空间之前定义一个新的函数,然后向它添加变量,比如这样。

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

    只要任何人使用由函数color授予的名称空间,一切都会好起来。如果你知道Java,这是一种旧的枚举:在这里我们只使用一个类或接口来保持静态属性。如果一个函数在javascript中是一种类,那么这个方法几乎是相同的。

    i事物是定义枚举的一种非常简单的方法。

    希望它有帮助!

    问候语。

    胜利者。


    您可以尝试使用https://bitback.org/snippets/frostbane/aajxm。

    1
    2
    3
    4
    5
    my.namespace.ColorEnum = new Enum(
       "RED = 0",
       "GREEN",
       "BLUE"
    )

    它应该达到IE8。