以下内容是否会使对象满足枚举在JavaScript中的所有特性?类似于:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
还是有其他方法可以做到这一点?
以下内容是否会使对象满足枚举在JavaScript中的所有特性?类似于:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
还是有其他方法可以做到这一点?
当前回答
IE8不支持冻结()方法。资料来源:http://kangax.github.io/compat-table/es5/,单击顶部的“显示过时的浏览器?”,然后检查IE8并冻结行列交叉点。
在我目前的游戏项目中,我使用了以下内容,因为很少有客户仍然使用IE8:
var CONST_WILD_TYPES = {
REGULAR: 'REGULAR',
EXPANDING: 'EXPANDING',
STICKY: 'STICKY',
SHIFTING: 'SHIFTING'
};
我们还可以做到:
var CONST_WILD_TYPES = {
REGULAR: 'RE',
EXPANDING: 'EX',
STICKY: 'ST',
SHIFTING: 'SH'
};
或者甚至是这样:
var CONST_WILD_TYPES = {
REGULAR: '1',
EXPANDING: '2',
STICKY: '3',
SHIFTING: '4'
};
最后一个,对于字符串来说似乎是最有效的,如果您让服务器和客户端交换这些数据,它会减少您的总带宽。当然,现在您有责任确保数据中没有冲突(RE、EX等必须唯一,1、2等也必须唯一)。请注意,为了向后兼容,您需要永远保持这些。
分配:
var wildType = CONST_WILD_TYPES.REGULAR;
对比:
if (wildType === CONST_WILD_TYPES.REGULAR) {
// do something here
}
其他回答
我制作了一个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>..."
我编写了enumeranjs一个非常小的库来解决这个问题,它确保了类型安全,允许枚举常量从原型继承,保证枚举常量和枚举类型是不可变的+许多小特性。它允许重构大量代码,并在枚举定义中移动一些逻辑。下面是一个示例:
var CloseEventCodes = new Enumeration("closeEventCodes", {
CLOSE_NORMAL: { _id: 1000, info: "Connection closed normally" },
CLOSE_GOING_AWAY: { _id: 1001, info: "Connection closed going away" },
CLOSE_PROTOCOL_ERROR: { _id: 1002, info: "Connection closed due to protocol error" },
CLOSE_UNSUPPORTED: { _id: 1003, info: "Connection closed due to unsupported operation" },
CLOSE_NO_STATUS: { _id: 1005, info: "Connection closed with no status" },
CLOSE_ABNORMAL: { _id: 1006, info: "Connection closed abnormally" },
CLOSE_TOO_LARGE: { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
console.log(this.info);
}
});
CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true
枚举基本上是一个工厂。
此处提供完整记录的指南。希望这有帮助。
更新
我认为我下面的答案不再是用JavaScript编写枚举的最佳方法。有关更多详细信息,请参阅我的博客文章: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"};
记住,枚举的字段(本例中的值、名称和代码)不需要用于身份检查,只是为了方便。此外,大小属性本身的名称不需要硬编码,也可以动态设置。因此,假设您只知道新枚举值的名称,您仍然可以毫无问题地添加它:
// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};
当然,这意味着无法再进行某些假设(例如,该值表示大小的正确顺序)。
记住,在JavaScript中,对象就像一个映射或哈希表。一组名称值对。你可以在事先不了解它们的情况下循环浏览它们或以其他方式操纵它们。
实例
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
尽管ES2015中只支持静态方法(而不支持静态财产)(请参见§15.2.2.2),但奇怪的是,您可以将以下内容用于Babel和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“enum”,而不是其他枚举(如果您使用了泛型对象或符号,则无法区分)。
使现代化
上面的代码有一个缺点,即它允许创建CellState类型的其他对象(尽管由于CellState被冻结,因此无法将它们分配给CellState的静态字段)。尽管如此,以下更精细的代码提供了以下优点:
不能再创建CellState类型的对象可以保证没有两个枚举实例分配了相同的代码从字符串表示中获取枚举的实用方法返回枚举的所有实例的values函数不必以上述手动(且容易出错)的方式创建返回值。“使用严格”;类状态{构造函数(code,displayName=code){if(Status.INSTANCES.has(代码))抛出新错误(`重复代码值:[${code}]`);if(!Status.canCreateMoreInstances)throw new Error(`尝试调用构造函数(${code}`+`,${displayName})在创建所有静态实例后`);this.code=代码;this.displayName=显示名称;对象.冻结(this);Status.INSTANCES.set(this.code,this);}到字符串(){return `[code:${this.code},displayName:${this.displayName}]`;}静态INSTANCES=新映射();静态canCreateMoreInstances=true;//值:静态ARCHIVED=新状态(“存档”);静态观察=新状态(“观察”);静态调度=新状态(“调度”);静态UNOBERVED=新状态(“未观察”);static UNTRIGGERED=新状态(“未触发”);静态值=函数(){return Array.from(Status.INSTANCES.values());}静态fromCode(代码){if(!Status.INSTANCES.has(代码))抛出新错误(“未知代码:${code}”);其他的return Status.INSTANCES.get(代码);}}Status.canCreateMoreInstances=false;对象冻结(状态);exports.Status=状态;
一种快速而简单的方法是:
var Colors = function(){
return {
'WHITE':0,
'BLACK':1,
'RED':2,
'GREEN':3
}
}();
console.log(Colors.WHITE) //this prints out "0"