JavaScript, defining unmodifiable Enumerations
So, I was inspired by this question ( Enums in JavaScript? ) to get to work on a library insert for JavaScript to enable unmodifiable Enumerations. I have a decently working method defined already, but I'd like to flesh it out more.
This concept makes use of Object.defineProperty
(Documentation: Here)
My current definition allows for :
var obj1 = {}, obj2 = {}, obj3 = {};
// Straight declaration (normal)
obj1.Enum('RED','BLUE','GREEN');
obj1.RED // == 0
obj1.BLUE // == 1
obj1.GREEN // == 2
// Buffer (padding) usage
obj2.Enum('RED','BLUE',null,undefined,'','GREEN');
obj2.RED // == 0
obj2.BLUE // == 1
obj2.GREEN // == 5
// Direct offset and case-correction
obj3.Enum('RED','BLUE',10,'gReEn','burnt orange');
obj3.RED // == 0
obj3.BLUE // == 1
obj3.GREEN // == 11
obj3.BURNT_ORANGE // == 12
What I have so far:
var odp=Object.defineProperty;
odp(Object.prototype,'Enum', {
value: function() {
var ignore=[undefined,null,''], n=0, args=arguments;
for(var i in args) {
if(ignore.indexOf(args[i])<0) {
if( typeof args[i]=="number") {
n=parseInt(args[i]);
} else {
try {
odp(this,String(args[i]).toUpperCase().replace(" ","_"), {
value:parseInt(n),enumerable:true
});
} catch(e) {
console.warn(e.message);
n--;
}
}
}
n++;
}
return this;
}
});
2 things I'd like to add to this are:
Object.defineProperty
where it's not defined. (I don't currently have access to the older browsers to test possible re-definitions) jsFiddle :
Note: The reason I have odp=Object.defineProperty
and args=arguments
is that I run my JavaScript through a closure compiler before inserting it into my pages, doing this assists with compression. (for those that may be wondering)
Older browser support: So, redefining Object.defineProperty
where it's not defined.
You can't do that with your current approach, because your current approach extends Object.prototype
. Extending Object.prototype
should very, very rarely be done if you have ES5 support, but it must never be done without it, because without Object.defineProperty
, you have no way to create a non-enumerable property. Adding enumerable properties to Object.property
will break all of your for-in
loops.
There's no reason for Enum
to be on the prototype anyway, though, just put it on Object
(or your own library object) and pass the object to be enum-ified into it.
Any considerations I may have missed it the Enum definition.
It seems to me that this offers little on top of:
var obj = Object.freeze({
"RED": 0,
"GREEN": 1,
"BLUE": 2
});
You might wrap that up into a function that will work, without freezing the object, in pre-ES5 environments, eg:
function createEnum(spec) {
if (Object.freeze) {
spec = Object.freeze(spec);
}
return spec;
}
You can enhance that, of course, if you want the automatic values, eg:
function createEnum(spec) {
var obj = {}, n;
if (Object.prototype.toString.call(spec) === "[object Array]") { // No isArray pre ES5
for (n = 0; n < spec.length; ++n) {
if (spec[n] != null) { // checks for null or undefined
obj[spec[n]] = n;
}
}
}
else {
for (n in spec) {
if (spec.hasOwnProperty(n)) {
obj[n] = spec[n];
}
}
}
if (Object.freeze) {
obj = Object.freeze(obj);
}
return obj;
}
You could put that on Object
( not Object.prototype
), if you liked.
Below you've said that you don't want to use freeze
because it makes the object non-extensible. Fair enough, wanting to add further enum values later. The above is easily adapted to doing that, using Object.defineProperty
if present and falling back on just assigning the property if not.
上一篇: 在JavaScript中使用枚举
下一篇: JavaScript,定义不可修改的枚举