以下内容是否会使对象满足枚举在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
}
还是有其他方法可以做到这一点?
当前回答
我一直在摆弄这个,因为我喜欢我的遗珠
使用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
});
由于属性可写:false,这应该使其类型安全。
因此,您应该能够创建一个自定义对象,然后对其调用Enum()。分配的值从0开始,每项递增。
var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED; // == 0
EnumColors.BLUE; // == 1
EnumColors.GREEN; // == 2
EnumColors.YELLOW; // == 3
其他回答
我修改了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
真的很像@Duncan上面所做的,但我不喜欢用Enum破坏全局Object函数空间,所以我写了以下内容:
function mkenum_1()
{
var o = new Object();
var c = -1;
var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};
for (i in arguments) {
var e = arguments[i];
if ((!!e) & (e.constructor == Object))
for (j in e)
f(j, (c=e[j]));
else
f(e, ++c);
}
return Object.freeze ? Object.freeze(o) : o;
}
var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');
console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);
// Output is:
// MED := 1
// LRG := 100
@Stijin也有一个简洁的解决方案(参考他的博客),其中包括这些对象的财产。我也为此写了一些代码,接下来我会把它包括在内。
function mkenum_2(seed)
{
var p = {};
console.log("Seed := " + seed);
for (k in seed) {
var v = seed[k];
if (v instanceof Array)
p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
else
p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
}
seed.properties = p;
return Object.freeze ? Object.freeze(seed) : seed;
}
此版本生成了一个允许友好名称转换和短代码的附加属性列表。我喜欢这个版本,因为不需要像代码那样在财产中重复数据输入。
var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });
这两者可以组合成一个处理单元mkenum(使用enum、分配值、创建和添加属性列表)。然而,由于我今天已经在这方面花了太多时间,我将把这个组合作为练习留给亲爱的读者。
你可以这样做
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
这是我对一个(标记的)Enum工厂的看法。这是一个工作演示。
/*
* Notes:
* The proxy handler enables case insensitive property queries
* BigInt is used to enable bitflag strings /w length > 52
*/
function EnumFactory() {
const proxyfy = {
construct(target, args) {
const caseInsensitiveHandler = {
get(target, key) {
return target[key.toUpperCase()] || target[key];
}
};
const proxified = new Proxy(new target(...args), caseInsensitiveHandler );
return Object.freeze(proxified);
},
}
const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);
const throwIf = (
assertion = false,
message = `Unspecified error`,
ErrorType = Error ) =>
assertion && (() => { throw new ErrorType(message); })();
const hasFlag = (val, sub) => {
throwIf(!val || !sub, "valueIn: missing parameters", RangeError);
const andVal = (sub & val);
return andVal !== BigInt(0) && andVal === val;
};
function EnumCtor(values) {
throwIf(values.constructor !== Array ||
values.length < 2 ||
values.filter( v => v.constructor !== String ).length > 0,
`EnumFactory: expected Array of at least 2 strings`, TypeError);
const base = BigInt(1);
this.NONE = BigInt(0);
values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );
}
EnumCtor.prototype = {
get keys() { return Object.keys(this).slice(1); },
subset(sub) {
const arrayValues = this.keys;
return new ProxiedEnumCtor(
[...sub.toString(2)].reverse()
.reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )
);
},
getLabel(enumValue) {
const tryLabel = Object.entries(this).find( value => value[1] === enumValue );
return !enumValue || !tryLabel.length ?
"getLabel: no value parameter or value not in enum" :
tryLabel.shift();
},
hasFlag(val, sub = this) { return hasFlag(val, sub); },
};
return arr => new ProxiedEnumCtor(arr);
}
你可以试试这个:
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');
}