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

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

// later on

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

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


当前回答

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

其他回答

底线:你不能。

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

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呢?这难道不会阻止你将星期一设置为星期四吗油炸四方饼

当然,Object.freeze可以完全解决我投诉的问题。我想提醒大家,当我写上述内容时,Object.freeze并不存在。

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

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

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

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

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

是否有人尝试过使用包含私有字段和“get”访问器的类执行此操作?我意识到私有类字段目前仍处于实验阶段,但它似乎可以用于创建具有不可变字段/财产的类。浏览器支持也不错。唯一不支持它的“主要”浏览器是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; }
}

现在您应该可以直接调用enums。

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

我刚刚发布了一个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和IE9+浏览器),返回的Enum对象是不可变的。

有关更多信息,请结账https://github.com/greenlaw110/enumjs

更新

我淘汰了gen_enum包,并将该函数合并到constjs包中,该包提供了更多功能,包括不可变对象、JSON字符串反序列化、字符串常量和位图生成等。签出https://www.npmjs.com/package/constjs了解更多信息

要从gen_enum升级到constjs,只需更改语句

var genEnum = require('gen_enum');

to

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

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

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

它应该工作到ie8。

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