在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楼
截至撰写本文时( 2014年10月) -这是一个当代的解决方案。 我正在将解决方案编写为节点模块,并且包括使用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中,对象就像是map或hash 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
的静态字段)。 尽管如此,以下更完善的代码仍具有以下优点:
- 不能再创建
CellState
类型的对象 - 您可以确保没有为两个枚举实例分配相同的代码
- 从字符串表示形式获取枚举的实用方法
返回上述枚举的所有实例的
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语句和循环中未使用的块。
'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
}
𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 
此外,这种枚举形式在最小化后也要快得多。 在普通的命名属性中,浏览器必须使用哈希图来查找该属性在对象上的位置。 尽管JIST编译器会智能地在对象上缓存该位置,但是对于特殊情况(例如从对象中删除较低的属性)仍然存在巨大的开销。
但是,使用连续的非稀疏整数索引的PACKED_ELEMENTS数组,由于已经指定了内部数组中值的索引,因此浏览器可以跳过很多开销。 是的,根据ECMAScript标准,所有属性都应视为字符串。 尽管如此,ECMAScript标准的这一方面在性能上还是极具误导性,因为所有浏览器都对数组中的数字索引进行了特殊的优化。
𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴
此外,我个人最 喜欢的是在Javascript模式下将这种枚举形式与CodeMirror文本编辑器一起使用。 CodeMirror的Javascript语法突出显示模式突出显示当前作用域中的局部变量。 这样,当您正确键入变量名称时,您会立即知道,因为如果以前使用var
关键字声明了变量名称,则变量名称会变成特殊的颜色(默认为青色)。 即使您不使用CodeMirror,执行带有错误类型的枚举名称的代码时,至少浏览器也会抛出有用的[variable name] is not defined
异常。 同样,JavaScript工具(例如JSLint和Closure Compiler)在告诉您何时键入枚举变量名称时非常吵闹。 CodeMirror,浏览器和各种Javascript工具一起使调试这种枚举形式变得非常简单而且非常容易。
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
确保类型安全性(使用枚举或其他方式)的程度更高的一种方法是使用诸如TypeScript或Flow之类的工具。
不需要引号,但为了保持一致性,我保留了它们。
#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');