以下内容是否会使对象满足枚举在JavaScript中的所有特性?类似于:

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

// later on

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

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


当前回答

这是我们都想要的:

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

其他回答

您只需要使用object.freeze(<your_object>)创建一个不可变的对象:

export const ColorEnum = Object.freeze({
    // you can only change the property values here
    // in the object declaration like in the Java enumaration
    RED: 0,
    GREEN: 1,
    BLUE: 2,
});

ColorEnum.RED = 22    // assigning here will throw an error
ColorEnum.VIOLET = 45 // even adding a new property will throw an error

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

我创建了一个类来存储和生成位掩码值。然后,我可以用这种方式使用伪常量位掩码值来测试,例如,如果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值属性,该属性是RGBA和RGBAM的4x5矩阵,其中M是乘数。得到的Ez财产为Ez.RR、Ez.RG等。

真的很像@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(使用enum、分配值、创建和添加属性列表)。然而,由于我今天已经在这方面花了太多时间,我将把这个组合作为练习留给亲爱的读者。

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

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

var CONST_WILD_TYPES = {
    REGULAR: 'REGULAR',
    EXPANDING: 'EXPANDING',
    STICKY: 'STICKY',
    SHIFTING: 'SHIFTING'
};

我们还可以做到:

var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

或者甚至是这样:

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

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

分配:

var wildType = CONST_WILD_TYPES.REGULAR;

对比:

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

这可能很有用:

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

实施简单高效:

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

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