在JavaScript中定义枚举的首选语法是什么? [关闭]

JavaScript中定义枚举的首选语法是什么? 就像是:

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

// later on

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

还是有更好的成语?


#1楼

如果您使用的骨干 ,你可以得到全面的枚举功能免费使用(通过ID,名称,自定义成员找到) Backbone.Collection

// 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()

#2楼

这是我使用的解决方案。

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

一些缺点:

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

#3楼

底线:不能。

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

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呢? 这样是否可以防止您将星期一设置为星期四? – Fry Quad

绝对, Object.freeze将完全解决我抱怨的问题。 我想提醒大家,当我写上面的文章时, Object.freeze实际上并不存在。

现在....现在它提供了一些非常有趣的可能性。

编辑2
这是一个用于创建枚举的很好的库。

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

尽管它可能不适合枚举的所有有效用法,但它却走了很长一段路。


#4楼

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

#5楼

我知道这是一个古老的方法,但是此后通过TypeScript接口实现的方法是:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Foo"] = 0] = "Foo";
    MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
    MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));

这使您可以查找两个MyEnum.Bar返回1,和MyEnum[1]返回“酒吧”,无论声明的顺序。


#6楼

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

我想使用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
}); 

make it type safe. 由于属性writable:false因此使其类型安全。

因此,您应该能够创建一个自定义对象,然后在其上调用Enum() 。 分配的值从0开始,每项递增。

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

#7楼

一种快速简单的方法是:

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

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

#8楼

截至撰写本文时( 201410月) -这是一个当代的解决方案。 我正在将解决方案编写为节点模块,并且包括使用Mocha和Chai以及underscoreJS进行的测试。 您可以轻松地忽略它们,如果需要的话,可以使用Enum代码。

看过很多文章,它们的库太复杂了。获得Java枚举支持的解决方案非常简单,实际上并不需要。 这是代码:

档案: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的模块化方法导出生成的Enum。

再一次,如果您只是休闲用户,或者在浏览器等中,只需将代码的工厂部分包括在内,如果您不希望在代码中使用下划线库,也可能会删除下划线库。


#9楼

你的答案太复杂了

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

#10楼

更新

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


警报名称已经可以:

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

另外,您可以使values对象成为对象,因此您也可以吃蛋糕:

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中,对象就像是maphash table 。 一组名称/值对。 您可以循环浏览它们或以其他方式操纵它们,而无需事先了解它们。

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


#11楼

var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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


#12楼

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

话虽这么说,因为值无关紧要(您使用过0、1、2),所以如果您想输出当前值,我将使用有意义的字符串。


#13楼

这是实现TypeScript枚举的几种不同方法。

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

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

#14楼

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

#15楼

在大多数现代浏览器中,有一种符号原始数据类型可用于创建枚举。 这将确保枚举的类型安全,因为JavaScript保证每个符号值都是唯一的,即Symbol() != Symbol() 。 例如:

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

为了简化调试,您可以在枚举值中添加描述:

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

柱塞演示

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

#16楼

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

#17楼

创建一个对象文字:

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

#18楼

我认为它很容易使用。 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"])); 


#19楼

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

您可以这样定义枚举:

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

现在,“ Days 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
    }

})();

#20楼

我写了enumerationjs一个很小的库来解决这个问题该问题确保类型安全 ,允许枚举常量继承自原型 ,保证枚举常量和枚举类型是不可变的+许多小功能。 它允许重构大量代码并在枚举定义内移动一些逻辑。 这是一个例子:

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基本上是一个工厂。

此处有完整的文档指南。 希望这可以帮助。


#21楼

我制作了一个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#中的Enums):

var val2 = enum1.item2;

要获取值的名称(将相同的值用于不同的名称时可能会模棱两可):

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

要获取具有对象中每个名称和值的数组:

var arr = enum1.GetObjArr();

将产生:

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

您还可以轻松获取html select选项:

var html = enum1.GetSelectOptionsHTML();

其中:

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

#22楼

你可以做这样的事情

    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


#23楼

尽管只有静态方法 (而不是静态的属性)在ES2015(见支持这里为好,§15.2.2.2),奇怪的是您可以用使用下面巴贝尔es2015预设:

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导入模块时,它也都可以按预期工作。

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

更新

上面的代码有一个缺陷,就是它允许人们创建CellState类型的其他对象(即使由于冻结而无法将它们分配给CellState的静态字段)。 尽管如此,以下更完善的代码仍具有以下优点:

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

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

#24楼

ES7中 ,您可以依靠静态属性进行优雅的ENUM:

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

然后

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

优点(使用类而不是文字对象)是拥有一个父类Enum然后您所有的Enum都将扩展该类。

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

#25楼

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

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'

码:

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(', ') }

}

#26楼

使用Javascript 代理

TLDR:将该类添加到实用程序方法中,并在整个代码中使用它,它可以模仿传统编程语言中的Enum行为,并且在您尝试访问不存在的枚举器或添加/更新枚举器时实际上会引发错误。 无需依赖Object.freeze()

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

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

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

完整说明:

从传统语言获得的枚举的一个非常有益的功能是,如果您尝试访问不存在的枚举器,它们会爆炸(抛出编译时错误)。

除了冻结模拟的枚举结构以防止意外/恶意添加其他值外,其他任何答案都没有解决枚举的内在功能。

您可能已经知道,访问JavaScript中不存在的成员只会返回undefined ,并且不会炸毁您的代码。 由于枚举数是预定义的常量(即一周中的几天),因此永远都不会出现未定义枚举数的情况。

不要误会我的意思,JavaScript访问未定义属性时返回undefined的行为实际上是语言的强大功能,但是当您尝试模拟传统Enum结构时,这并不是您想要的功能。

这是代理对象发光的地方。 通过引入ES6(ES2015),代理以该语言进行了标准化。 这是MDN的说明:

Proxy对象用于定义基本操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。

与Web服务器代理类似,JavaScript代理能够拦截对对象的操作(使用“陷阱”,如果愿意,可以称其为“钩子”),并允许您在对象完成之前执行各种检查,操作和/或操纵(或在某些情况下,如果我们尝试引用不存在的枚举数,则完全停止操作,这正是我们要执行的操作。

这是一个使用Proxy对象模仿Enums的人为例子。 此示例中的枚举器是标准的HTTP方法(即“ GET”,“ POST”等):

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


ASIDE:代理到底是什么?

我记得当我第一次到处都看到代理这个词时,很长一段时间对我来说绝对没有意义。 如果现在就是您,我认为将代理普遍化的一种简单方法是将它们视为软件,机构,甚至是充当两个服务器,公司或人员之间的中介或中间人的人员。


#27楼

这是Typescript将其enum转换为Javascript的方式:

var makeEnum = function(obj) {
    obj[ obj['Active'] = 1 ] = 'Active';
    obj[ obj['Closed'] = 2 ] = 'Closed';
    obj[ obj['Deleted'] = 3 ] = 'Deleted';
}

现在:

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

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

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

#28楼

𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦™𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦

让我们直接解决问题:文件大小。 此处列出的所有其他答案都会使您的代码膨胀到极致。 我向您介绍,为了实现最佳性能,代码的可读性,大规模项目管理,许多代码编辑器中的语法提示以及通过最小化来减小代码大小,这是进行枚举的正确方法:下划线表示变量。



下划线符号变量

const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;

// later on

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

这就是我记得何时使用INDEX_和何时使用ENUM_

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

但是,在某些情况下,例如在计算每个项目的出现次数时, ENUM_可能适合作为索引。

 const ENUM_PET_CAT = 0, ENUM_PET_DOG = 1, ENUM_PET_RAT = 2, ENUMLEN_PET = 3; var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT, ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT, ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG]; var petsFrequency = []; for (var i=0; i<ENUMLEN_PET; i=i+1|0) petsFrequency[i] = 0; for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0) petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0; console.log({ "cat": petsFrequency[ENUM_PET_CAT], "dog": petsFrequency[ENUM_PET_DOG], "rat": petsFrequency[ENUM_PET_RAT] }); 

观察一下,在上面的代码中,添加一种新的宠物真的很容易:您只需要在ENUM_PET_RAT之后添加一个新条目并相应地更新ENUMLEN_PET 。 在其他枚举系统中添加新条目可能会更加困难且容易出错。



𝗘𝘅𝘁𝗲𝗻𝗱𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀𝗪𝗶𝘁𝗵𝗔𝗱𝗱𝗶𝘁𝗶𝗼𝗻

另外,此枚举语法允许清晰简洁的类扩展,如下所示。 要扩展类,请向父类的LEN_条目添加一个递增的数字。 然后,使用其自己的LEN_条目结束子类,以便将来可以进一步扩展子类。

附加扩展图

 (function(window){ "use strict"; var parseInt = window.parseInt // use INDEX_ when representing the index in an array instance const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE INDEXLEN_PIXELCOLOR = 1, INDEX_SOLIDCOLOR_R = INDEXLEN_PIXELCOLOR+0, INDEX_SOLIDCOLOR_G = INDEXLEN_PIXELCOLOR+1, INDEX_SOLIDCOLOR_B = INDEXLEN_PIXELCOLOR+2, INDEXLEN_SOLIDCOLOR = INDEXLEN_PIXELCOLOR+3, INDEX_ALPHACOLOR_R = INDEXLEN_PIXELCOLOR+0, INDEX_ALPHACOLOR_G = INDEXLEN_PIXELCOLOR+1, INDEX_ALPHACOLOR_B = INDEXLEN_PIXELCOLOR+2, INDEX_ALPHACOLOR_A = INDEXLEN_PIXELCOLOR+3, INDEXLEN_ALPHACOLOR = INDEXLEN_PIXELCOLOR+4, // use ENUM_ when representing a mutually-exclusive species or type ENUM_PIXELTYPE_SOLID = 0, ENUM_PIXELTYPE_ALPHA = 1, ENUM_PIXELTYPE_UNKNOWN = 2, ENUMLEN_PIXELTYPE = 2; function parseHexColor(inputString) { var rawstr = inputString.trim().substring(1); var result = []; if (rawstr.length === 8) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA; result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16); result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16); result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16); result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16); } else if (rawstr.length === 4) { result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11; result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11; result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11; result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11; } else if (rawstr.length === 6) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID; result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16); result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16); result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16); } else if (rawstr.length === 3) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID; result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11; result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11; result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11; } else { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN; } return result; } // the red component of green console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]); // the alpha of transparent purple console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); // the enumerated array for turquoise console.log(parseHexColor("#40E0D0")); })(self); 

(长度:2,450字节)

有人可能会说这比其他解决方案不太实用:它占用了很多空间,需要花费很长时间来编写,并且没有使用糖语法。 如果他们不缩减代码,那么这些人将是正确的。 但是,没有理性的人会在最终产品中留下未缩小的代码。 对于这种缩小,Closure Compiler是我尚未找到的最好的。 在线访问可以在这里找到。 Closure编译器能够获取所有这些枚举数据并将其内联,从而使您的Javascript变得超级精简,并且能够快速运行超级精简。 因此,使用Closure Compiler进行压缩。 观察一下。



𝗪𝗶𝘁𝗵𝗠𝗶𝗻𝗶𝗳𝘆 𝗖𝗹𝗼𝘀𝘂𝗿𝗲𝗖𝗼𝗺𝗽𝗶𝗹𝗲𝗿

Closure编译器能够通过推理执行一些非常令人难以置信的优化,这些推理远远超出了其他Javascript缩小器的能力。 Closure Compiler能够内联设置为固定值的基本变量。 Closure Compiler还能够基于这些内联值进行推断,并消除if语句和循环中未使用的块。

通过Closure编译器拧代码

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

(长度:605字节)



𝗦𝗶𝘇𝗲

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

不使用枚举的源 (长度:1,973字节(比枚举代码短477字节!))
在不使用枚举的情况下缩小 (长度:843字节( 比枚举代码长 238字节))

代码大小表



可以看出,没有枚举,源代码会更短,但代价是较大的缩小代码。 我对你一无所知; 但是我确定我不会将源代码包含到最终产品中。 因此,这种枚举形式非常优越,因为它导致较小的缩小文件大小。



𝗖𝗼𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝘃𝗲🤝𝗕𝘂𝗴𝗙𝗶𝘅𝗶𝗻𝗴

这种枚举形式的另一个优点是,它可用于轻松管理大型项目,而无需牺牲最小化的代码大小。 在与许多其他人一起从事大型项目时,最好使用创建代码的人来显式标记和标记变量名,这样可以快速识别出代码的原始创建者以进行协作的错误修复,这可能是有益的。

// 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(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 OoOrv.png

此外,这种枚举形式在最小化后也要快得多。 在普通的命名属性中,浏览器必须使用哈希图来查找该属性在对象上的位置。 尽管JIST编译器会智能地在对象上缓存该位置,但是对于特殊情况(例如从对象中删除较低的属性)仍然存在巨大的开销。

lattice.svg

但是,使用连续的非稀疏整数索引的PACKED_ELEMENTS数组,由于已经指定了内部数组中值的索引,因此浏览器可以跳过很多开销。 是的,根据ECMAScript标准,所有属性都应视为字符串。 尽管如此,ECMAScript标准的这一方面在性能上还是极具误导性,因为所有浏览器都对数组中的数字索引进行了特殊的优化。



𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

此外,我个人 喜欢的是在Javascript模式下将这种枚举形式与CodeMirror文本编辑器一起使用。 CodeMirror的Javascript语法突出显示模式突出显示当前作用域中的局部变量。 这样,当您正确键入变量名称时,您会立即知道,因为如果以前使用var关键字声明了变量名称,则变量名称会变成特殊的颜色(默认为青色)。 即使您不使用CodeMirror,执行带有错误类型的枚举名称的代码时,至少浏览器也会抛出有用的[variable name] is not defined异常。 同样,JavaScript工具(例如JSLint和Closure Compiler)在告诉您何时键入枚举变量名称时非常吵闹。 CodeMirror,浏览器和各种Javascript工具一起使调试这种枚举形式变得非常简单而且非常容易。

CodeMirror高亮演示

 const ENUM_COLORENUM_RED = 0, ENUM_COLORENUM_GREEN = 1, ENUM_COLORENUM_BLUE = 2, ENUMLEN_COLORENUM = 3; var currentColor = ENUM_COLORENUM_GREEN; if(currentColor === ENUM_COLORENUM_RED) { // whatever } if(currentColor === ENUM_COLORENUM_DNE) { // whatever } 

在上述代码段中,由于不存在ENUM_COLORENUM_DNE ,因此向您发出错误ENUM_COLORENUM_DNE



☑☑

因此,我得出结论,确实,这种枚举形式不仅是减小代码大小的最佳方法,而且还是性能,调试和协作的最佳方法。


#29楼

从1.8.5开始,可以密封和冻结对象,因此将以上定义为:

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之类的工具。

资源

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


#30楼

这就是我们所有人想要的:

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

通过这样做,可以按常规方式访问常量(是,是,Color.GREEN),并且它们会得到一个连续的整数值(否= 0,是= 1;红色= 0,绿色= 1,蓝色= 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');
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值