以下内容是否会使对象满足枚举在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、分配值、创建和添加属性列表)。然而,由于我今天已经在这方面花了太多时间,我将把这个组合作为练习留给亲爱的读者。

其他回答

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
}

在我看来,枚举是什么?它是一个不可变的对象,总是可以访问,您可以相互比较项目,但项目具有通用的财产/方法,但对象本身或值不能更改,它们只能实例化一次。

枚举用于比较数据类型、设置、采取/回复的操作等。

为此,您需要具有相同实例的对象,以便您可以检查它是否为枚举类型if(something instanceof enum)此外,如果您获得了一个枚举对象,则无论枚举类型如何,它都应该以相同的方式进行响应。

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

请记住,它是javascript,不提供固定的枚举类型,您最终总是自己实现,正如本线程所示,有很多实现都不正确。


这是我用于枚举的内容。由于枚举是不可变的(或者至少应该是呵呵),所以我冻结了对象,这样它们就不容易被操作了。

枚举可以由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"))});

你的答案太复杂了

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

你可以试试这个:

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

我编写了enumeranjs一个非常小的库来解决这个问题,它确保了类型安全,允许枚举常量从原型继承,保证枚举常量和枚举类型是不可变的+许多小特性。它允许重构大量代码,并在枚举定义中移动一些逻辑。下面是一个示例:

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

枚举基本上是一个工厂。

此处提供完整记录的指南。希望这有帮助。