【问题标题】:How can I guarantee that my enums definition doesn't change in JavaScript?如何保证我的枚举定义在 JavaScript 中不会改变?
【发布时间】:2010-09-22 05:18:33
【问题描述】:

以下是否会使对象满足枚举在 JavaScript 中的所有特征?比如:

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

// later on

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

或者还有其他方法可以做到这一点吗?

【问题讨论】:

  • 不要使用0 作为枚举数。除非它用于尚未设置的东西。当使用== 进行比较时,JS 将false || undefined || null || 0 || "" || '' || NaN 视为相同的值。
  • @matsko 不只是反对使用 == 的论据吗?
  • 0 == null 返回错误
  • 但是false == 0+null == 0(有时会在您不期望的情况下发生转换为数字),而null == undefined 也是,+undefinedNaN(尽管@987654331 @)。
  • 双重等式矩阵比microsoft word的自动格式化更混乱

标签: javascript


【解决方案1】:

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

话虽如此,因为值是什么并不重要(您使用过 0、1、2),如果您想输出当前值,我会使用有意义的字符串。

【讨论】:

  • 这在另一个答案中有所说明,但由于这个答案是公认的答案,我会在这里发布。 OP的解决方案是正确的。不过,如果与Object.freeze() 一起使用会更好。这将防止其他代码更改枚举的值。示例:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
  • @TolgaE 谢谢你的图书馆!它启发了我不仅将其归结为最低限度,而且还添加了一些功能!我已经把你的分叉了,把它都放在这里:github.com/BlueHuskyStudios/Micro-JS-Enum
  • @Supuhstar 太好了!很高兴你可以使用它。如果你想将它合并到这个库中,请随时提出拉取请求,然后我可以更新 npm 库
  • 如果有人感兴趣,我有 implemented 类型安全枚举,类似于它们在 Java 中的方式。这意味着您可以进行instanceof 检查。例如ColorEnum.RED instanceof ColorEnum(返回true)。您还可以使用名称 ColorEnum.fromName("RED") === ColorEnum.RED 解析实例(返回 true)。每个实例还有一个.name() 和一个.ordinal() 方法,而枚举本身也有一个values() 方法,它返回一个包含所有常量的数组。
  • 我不确定我是否同意“有意义的字符串”的建议。不应将枚举视为字符串或数字;它们是抽象数据类型。如果没有一些辅助方法,应该不可能“输出当前值”。在 Java 和 .NET 中,它是 ToString() 方法。我们 JS 开发人员已经太依赖“正常工作”的东西了!此外,应该能够在枚举上快速switch。比较字符串比比较数字要慢,所以如果使用字符串而不是整数,switch 的性能会稍差。
【解决方案2】:

底线:你不能。

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

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

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

【讨论】:

  • javascript 中有类型安全吗?
  • 所以不要将值映射到对象属性。使用 getter 访问枚举数(存储为“私有”对象的属性)。一个天真的实现看起来像 - var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
  • @Scott Evernden:要点。 @kangax:关键是它仍然是一个黑客。枚举根本不存在于 Javascript、句号、故事结尾。即使是 Tim Sylvester 建议的模式,也仍然不是很理想。
  • 用文字洒在代码上不是很容易维护,因此为它创建常量是有意义的。当然 Javascript 也没有常量。所以基本上这只是一种编写干净代码的方法。它不能被强制执行,但在 Javascript 中却不多。您可以重新定义常量或函数,或者大部分内容。 EG:document.getElementById = function() {alert("你搞砸了。Javascript 不是类型安全的。");};
  • @Randolpho:Artur Czajka 的 Object.freeze 怎么样?这不会阻止您将星期一设置为星期四吗?
【解决方案3】:

更新

感谢大家的所有支持,但我认为我下面的回答不再是用 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 中,对象就像 map哈希表。一组名称-值对。您可以在事先不了解它们的情况下循环浏览它们或以其他方式操纵它们。

示例

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

【讨论】:

  • 那么,如果只有名称,您将如何创建简单的 SIZE?
  • @Johanisma:这个用例对枚举没有意义,因为它们的整个想法是你事先知道所有的值。但是,没有什么能阻止您稍后在 Javascript 中添加额外的值。我将在我的答案中添加一个示例。
  • +1 用于使用属性方法链接到您的帖子。优雅之处在于基本声明很简单,就像在 OP 中一样,在需要时添加了属性功能。
  • @Stijin,真的很喜欢你更新的解决方案。在您的博客上的 cmets 中发布代码,并在下面作为评论。基本上,使用一个函数,从现有的哈希列表执行属性构建,并可选择冻结它(我的列表中的 mkenum_2)。干杯。
  • 还有一个实现它的库,还包括比较和反向搜索的好功能:github.com/adrai/enum
【解决方案4】:

从1.8.5开始可以seal and freeze the object,所以上面定义为:

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

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

瞧! JS 枚举。

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

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

确保更高程度的类型安全(使用枚举或其他方式)的一种方法是使用TypeScriptFlow 之类的工具。

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

【讨论】:

  • 根据 Wikipedia (en.wikipedia.org/wiki/JavaScript#Versions),它适用于 Firefox 4、IE 9、Opera 11.60,我知道它适用于 Chrome。
  • 这是 2012 年的正确答案。更简单:var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });。您无需指定 id,只需使用空对象来比较枚举即可。 if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
  • 为了向后兼容,if (Object.freeze) { Object.freeze(DaysEnum); }
  • 我想指出,做 ({ monday: {}, 等意味着如果你通过 stringify 将该对象转换为 JSON,你将得到 [{"day": {}}] 这不会起作用。
  • @Supuhstar 我现在对这个问题的看法不同了。不要使用 freeze(),它完全没用,而且浪费时间做“愚蠢”的事情。如果你想公开一个枚举,只需公开这个:var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}。像我之前的评论那样比较对象比比较数字要慢得多。
【解决方案5】:

这是我们都想要的:

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


编辑 - 小改进 - 现在使用可变参数:(不幸的是,它在 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');

【讨论】:

【解决方案6】:

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

【讨论】:

    【解决方案7】:

    这是我使用的解决方案。

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

    一些缺点:

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

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        我知道这是一个旧的,但它通过 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],而不管声明的顺序如何。

        【讨论】:

        • Plus MyEnum["Bar"] 工作返回 1...
        • 当然,如果你真的在使用 Typescript:enum MyEnum { Foo, Bar, Foobar }
        【解决方案10】:

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

        使用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
        

        【讨论】:

        • 如果在枚举末尾添加return this;,您可以这样做:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
        • 我没有考虑到这一点,因为这不是我正常的做事方式。但你是绝对正确的!我将在其中进行编辑。
        • 我真的很喜欢这个,虽然我不喜欢搞砸对象空间(使用全局函数 ENUM)。将其转换为 mkenum 函数并添加可选的数字赋值 => var mixedUp = mkenum('BLACK', {RED: 0x0F00, BLUE: 0X0F, GREEN: 0x0F0, WHITE: 0x0FFF, ONE: 1}, TWO, THREE, FOUR) ; // 添加我的代码作为下面的答案。谢谢。
        • 说实话,我什至不再使用这个了。我一直在使用 Google 的 Closure Compiler,如果您使用 Advanced 设置,它就不能很好地工作(或者它只会使事情复杂化)。所以我刚刚回到标准对象表示法。
        • falsewritableenumerableconfigurable 的默认值。无需咀嚼默认设置。
        【解决方案11】:

        我不久前使用了__defineGetter____defineSetter__defineProperty 的混合使用,具体取决于JS 版本。

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

        你会这样使用它:

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

        它会创建一个不可变的 string:int 字典(一个枚举)。

        【讨论】:

        • 好的,我重写了它,现在它与库无关(虽然它假设某种 AMD 加载器,但可以删除)。
        【解决方案12】:

        一种快速简单的方法是:

        var Colors = function(){
        return {
            'WHITE':0,
            'BLACK':1,
            'RED':2,
            'GREEN':3
            }
        }();
        
        console.log(Colors.WHITE)  //this prints out "0"
        

        【讨论】:

        • 该功能是不必要的,并为您提供与 OP 发布的完全相同的结果。
        【解决方案13】:

        截至撰写本文时,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;
        

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

        文件: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 的模块化方法导出这些生成的枚举。

        再一次,如果您只是普通用户,或者在浏览器等中,只需使用代码的工厂部分,如果您不想在代码中使用下划线库,也可能会删除它。

        【讨论】:

        • 您能否仅以“作为一个只想要枚举,而不是工厂、下划线或任何花哨的东西的临时用户的身份,如何做到这一点”发布一个答案?
        • 尽管从开发人员的角度来看这非常棒,但它不是很干净或可读。来自 OP 的 Enum 解决方案在各个方面都更容易、更易读,因此更好用。不过,你想出这个真是太棒了。
        【解决方案14】:

        你的答案太复杂了

        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
        

        【讨论】:

        • @JackGiffin 我同意您的答案性能更高,并且我的答案可能会占用更多内存,尽管您不应该假设每个人都想要 C++ 实现它的方式的枚举。请尊重其他答案和可能更喜欢这个答案的开发人员。
        【解决方案15】:

        真的很喜欢上面@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,(使用枚举、赋值、创建和添加属性列表)。但是,由于我今天已经在这方面花费了太多时间,所以我将把这个组合作为练习留给亲爱的读者。

        【讨论】:

          【解决方案16】:
          var ColorEnum = {
              red: {},
              green: {},
              blue: {}
          }
          

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

          【讨论】:

          • 这个答案被低估了。它的简单性是我最喜欢的想法之一。在实践中,我认为我会坚持使用字符串,因为它现在更容易调试。
          • 嗯,只要确保这段代码不会被调用两次......
          【解决方案17】:

          你可以使用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"))

          【讨论】:

            【解决方案18】:

            这里有几种不同的方法来实现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
            

            【讨论】:

              【解决方案19】:

              我的意见中的枚举是什么:它是一个始终可访问的不可变对象,您可以将项目相互比较,但项目具有共同的属性/方法,但对象本身或值不能更改,它们只被实例化一次。

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

              因此,为此您需要具有相同实例的对象,以便检查它是否为枚举类型 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"))});
              

              【讨论】:

                【解决方案20】:

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

                【讨论】:

                  【解决方案21】:
                  var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });
                  

                  你不需要指定一个id,你可以使用一个空对象来比较枚举。

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

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

                  【讨论】:

                  • {red:{green:{blue:{}}}}.unwindme()!
                  • 请试试这个:if (JSON.parse(JSON.stringify(DaysEnum.monday)) != DaysEnum.monday) console.error('Oops!')。请阅读我的博文Enums in Javascript,这正是关于这个问题的,以了解为什么会发生这种情况。
                  【解决方案22】:

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

                  【讨论】:

                  • 这是理论上的正确答案。实际上,2015 浏览器支持还远远不够。到目前为止还没有准备好生产。
                  • 虽然目前还没有浏览器支持,但这是最好的答案,因为这接近于 Symbol 的用途。
                  • 嗯...虽然枚举值通常需要可序列化,但符号序列化和反序列化并不那么方便。
                  • 只有我还是 Object.freeze 只针对那些不接受“猴子补丁自担风险”是 JS 的社会契约这一事实的人?
                  【解决方案23】:

                  IE8 不支持 freeze() 方法。
                  来源:http://kangax.github.io/compat-table/es5/,点击“显示过时的浏览器?”在顶部,并检查 IE8 & freeze row 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
                  }
                  

                  【讨论】:

                    【解决方案24】:

                    创建一个对象字面量:

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

                    【讨论】:

                    • const 不会使对象的属性不可变,它只意味着变量Modes 不能重新分配给其他东西。为了使其更完整,请使用Object.freeze()const
                    • 请不要使用Object.freeze。它可以防止 Closure Compiler 内联对象。
                    【解决方案25】:

                    我认为它很容易使用。 https://stackoverflow.com/a/32245370/4365315

                    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)。

                    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, "<br>");
                    
                    
                    enumA.setType("200L", false);
                    document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
                    
                    enumA.setDefaultType();
                    document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
                    
                    
                    enumA.setTypeByIndex(1);
                    document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
                    
                    document.writeln("is equals = ", (enumA.Value == TypeA["2"]));

                    【讨论】:

                      【解决方案26】:

                      我提出了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
                          }
                      
                      })();
                      

                      【讨论】:

                      • 看起来不错,也许您应该检查是否存在 freeze 方法以实现向后兼容性?例如,if (Object.freeze) { Object.freeze(values); }
                      【解决方案27】:

                      我写了 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.希望这会有所帮助。

                      【讨论】:

                      • 这不是真正的枚举。但我可以看到这非常有用。
                      • @Will,你怎么称呼他们^^? AugmentedEnum ?
                      • 我不确定...它是枚举 + 元数据的混合(在您的示例中)。枚举只有一个名称和一个结构值,它可以转换为/从。您可以使用它在每个属性上放置无限数量的属性。我以前见过这种模式,编写它的团队将其称为结果常量......包括单独的常量类型,例如 DataType,它不仅有一个名称,而且有一个 ID 值,有效的运算符(另一组常量! )、有效的转换等。非常有用,但不仅仅是简单的枚举。
                      • 表现很差。使所有代码变慢。使缩小的代码大几倍。非常糟糕。
                      【解决方案28】:

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

                      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# 中的枚举):

                      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>..."
                      

                      【讨论】:

                        【解决方案29】:

                        你可以试试这个:

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

                        【讨论】:

                          【解决方案30】:

                          你可以这样做

                              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

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2021-04-08
                            • 1970-01-01
                            • 1970-01-01
                            • 2018-10-12
                            • 2018-10-12
                            • 2016-07-08
                            相关资源
                            最近更新 更多