यह बिल्कुल सही तरीका है जो मुझे पता है कि अधिकांश भाषाओं में enums काम करते हैं। आम तौर पर वे इन राज्यों में से एक के रूप में मूल्य टाइप करने के तरीके की तरह हैं। संभावित मूल्यों के एक सेट से एक मान का चयन करने की तरह। और सादे पूर्णांक के विपरीत, ऐसा करने में टाइप-सुरक्षा सुनिश्चित करने के लिए।
आपने अपने कोड में क्या पोस्ट किया है, मैं कारखाने के तरीकों के साथ एक सादा वस्तु कहूंगा।
चूंकि उन्हें उस भाषा द्वारा समर्थित नहीं किया गया है, जिस तरह से आपको उन्हें लागू करना है ताकि आपकी आवश्यकताओं को यथासंभव उपयुक्त हो सके। तो आप किस व्यवहार की अपेक्षा करते हैं।
इस बीच में त्वरित विवरणों पर दिए गए विवरणों के आधार पर एक कार्यान्वयन। उम्मीद है कि यह आपके अपेक्षा के करीब आता है:
var odp = {
ENUMERABLE: 4,
//two helper with Object.defineProperty.
value: function(obj, prop, v, flags){
this.configurable = Boolean(flags & odp.CONFIGURABLE);
this.writable = Boolean(flags & odp.WRITABLE);
this.enumerable = Boolean(flags & odp.ENUMERABLE);
this.value = v;
Object.defineProperty(obj, prop, this);
this.value = null; //v may be a function or an object: remove the reference
return obj;
}.bind({ //caching the basic definition
value: null,
configurable: false,
writable: false,
enumerable: false
}),
accessor: function(obj, prop, getter, setter){
this.get = getter || undefined;
this.set = setter || undefined;
Object.defineProperty(obj, prop, this);
this.get = null;
this.set = null;
return obj;
}.bind({ get: null, set: null })
}
//make these values immutable
odp.value(odp, "CONFIGURABLE", 1, odp.ENUMERABLE);
odp.value(odp, "WRITABLE", 2, odp.ENUMERABLE);
odp.value(odp, "ENUMERABLE", 4, odp.ENUMERABLE);
//Policy:
//1. I don't f*** care wether the keys on the definition are own or inherited keys.
//since you pass them to me, I suppose you want me to process them.
//2. If i find some undefined-value i ignore it, as if it wasn't there.
//use null to represent some "empty" value
//name and extendProto are optional
function Enum(name, description, extendProto){
var n = name, d = description, xp=extendProto;
if(n && typeof n === "object") xp=d, d = n, n = null;
var xpf = typeof xp === "function" && xp;
var xpo = typeof xp === "object" && xp;
function type(){
throw new Error("enums are not supposed to be created manually");
}
//abusing filter() as forEach()
//removing the keys that are undefined in the same step.
var keys = Object.keys(d).filter(function(key){
var val = d[key];
if(val === undefined) return false;
var proto = Object.create(type.prototype);
//your chance to extend the particular prototype with further properties
//like adding the prototype-methods of some other type
var props = xpf || xpo && xpo[key];
if(typeof props === "function")
props = props.call(type, proto, key, val);
if(props && typeof props === "object" && props !== proto && props !== val){
var flags = odp.CONFIGURABLE+odp.WRITABLE;
for(var k in props) props[k]===undefined || odp.value(proto, k, props[k], flags);
if("length" in props) odp.value(props, "length", props.length, flags);
}
if(typeof val === "function"){
//a factory and typedefinition at the same type
//call this function to create a new object of the type of this enum
//and of the type of this function at the same time
type[key] = function(){
var me = Object.create(proto);
var props = val.apply(me, arguments);
if(props && typeof props === "object" && props !== me){
for(var k in props) props[k]===undefined || odp.value(me, k, props[k], odp.ENUMERABLE);
if("length" in props) odp.value(me, "length", props.length);
}
return me;
}
//fix the fn.length-property for this factory
odp.value(type[key], "length", val.length, odp.CONFIGURABLE);
//change the name of this factory
odp.value(type[key], "name", (n||"enum")+"{ "+key+" }" || key, odp.CONFIGURABLE);
type[key].prototype = proto;
odp.value(proto, "constructor", type[key], odp.CONFIGURABLE);
}else if(val && typeof val === "object"){
for(var k in val) val[k] === undefined || odp.value(proto, k, val[k]);
if("length" in val) odp.value(proto, "length", val.length);
type[key] = proto;
}else{
//an object of the type of this enum that wraps the primitive
//a bit like the String or Number or Boolean Classes
//so remember, when dealing with this kind of values,
//you don't deal with actual primitives
odp.value(proto, "valueOf", function(){ return val; });
type[key] = proto;
}
return true;
});
odp.value(type, "name", n || "enum[ " + keys.join(", ") + " ]", odp.CONFIGURABLE);
Object.freeze(type);
return type;
}
सावधान रहें, इस कोड को कुछ और संशोधन की आवश्यकता हो सकती है। उदाहरण:
कारखानों
function uint(v){ return v>>>0 }
var Barcode = Enum("Barcode", {
QRCode: function(string){
//this refers to an object of both types, Barcode and Barcode.QRCode
//aou can modify it as you wish
odp.value(this, "valueOf", function(){ return string }, true);
},
UPCA: function(a,b,c,d){
//you can also return an object with the properties you want to add
//and Arrays, ...
return [
uint(a),
uint(b),
uint(c),
uint(d)
];
//but beware, this doesn't add the Array.prototype-methods!!!
//event this would work, and be processed like an Array
return arguments;
},
Other: function(properties){
return properties; //some sugar
}
});
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3);
console.log("productBarcode is Barcode:", productBarcode instanceof Barcode); //true
console.log("productBarcode is Barcode.UPCA:", productBarcode instanceof Barcode.UPCA); //true
console.log("productBarcode is Barcode.Other:", productBarcode instanceof Barcode.Other); //false
console.log("accessing values: ", productBarcode[0], productBarcode[1], productBarcode[2], productBarcode[3], productBarcode.length);
Array.prototype.forEach.call(productBarcode, function(value, index){
console.log("index:", index, " value:", value);
});
वस्तुओं और प्रिमिटिव
var indices = Enum({
lo: { from: 0, to: 13 },
hi: { from: 14, to: 42 },
avg: 7
});
var lo = indices.lo;
console.log("lo is a valid index", lo instanceof indices);
console.log("lo is indices.lo", lo === indices.lo);
//indices.lo always references the same Object
//no function-call, no getter!
var avg = indices.avg; //beware, this is no primitive, it is wrapped
console.log("avg is a valid index", avg instanceof indices);
console.log("comparison against primitives:");
console.log(" - typesafe", avg === 7); //false, since avg is wrapped!!!
console.log(" - loose", avg == 7); //true
console.log(" - typecast+typesafe", Number(avg) === 7); //true
//possible usage like it was a primitive.
for(var i=lo.from; i<lo.to; ++i){
console.log(i, i == avg); //take a look at the first output ;)
}
//but if you want to use some of the prototype methods
//(like the correct toString()-method on Numbers, or substr on Strings)
//make sure that you have a proper primitive!
var out = avg.toFixed(3);
//will fail since this object doesn't provide the prototype-methods of Number
//+avg does the same as Number(avg)
var out = (+avg).toFixed(3); //will succeed
पहचान
var def = { foo: 42 };
var obj = Enum({
a: 13,
b: 13,
c: 13,
obj1: def,
obj2: def
});
//although all three have/represent the same value, they ain't the same
var v = obj.a;
console.log("testing a", v === obj.a, v === obj.b, v===obj.c); //true, false, false
var v = obj.b;
console.log("testing a", v === obj.a, v === obj.b, v===obj.c); //false, true, false
var v = obj.c;
console.log("testing a", v === obj.a, v === obj.b, v===obj.c); //false, false, true
console.log("comparing objects", obj.obj1 === obj.obj2); //false
console.log("comparing property foo", obj.obj1.foo === obj.obj2.foo); //true
//same for the values provided by the factory-functions:
console.log("compare two calls with the same args:");
console.log("Barcode.Other() === Barcode.Other()", Barcode.Other() === Barcode.Other());
//will fail, since the factory doesn't cache,
//every call creates a new Object instance.
//if you need to check wether they are equal, write a function that does that.
extendProto
//your chance to extend the prototype of each subordinated entry in the enum
//maybe you want to add some method from some other prototype
//like String.prototype or iterator-methods, or a method for equality-checking, ...
var Barcode = Enum("Barcode", {/* factories */}, function(proto, key, value){
var _barcode = this;
//so you can access the enum in closures, without the need for a "global" variable.
//but if you mess around with this, you are the one to debug the Errors you produce.
//this function is executed right after the prototpe-object for this enum-entry is created
//and before any further modification.
//neither this particular entry, nor the enum itself are done yet, so don't mess around with them.
//the only purpose of this method is to provide you a hook
//to add further properties to the proto-object
//aou can also return an object with properties to add to the proto-object.
//these properties will be added as configurable and writable but not enumerable.
//and no getter or setter. If you need more control, feel free to modify proto on you own.
return {
isBarcode: function(){
return this instanceof _barcode;
}
}
});
//OR you can define it for every single property,
//so you don't have to switch on the different properties in one huge function
var Barcode = Enum("Barcode", {/* factories */}, {
"UPCA": function(proto, key, value){
//same behaviour as the universal function
//but will be executed only for the proto of UPCA
var _barcode = this; //aka Barcode in this case
var AP = [];
return {
//copy map and indexOf from the Array prototype
map: AP.map,
indexOf: AP.indexOf,
//and add a custom toString and clone-method to the prototype
toString: function(){
return "UPCA[ "+AP.join.call(this, ", ")+" ]";
},
clone: function(){
return _barcode.UPCA.apply(null, this);
}
};
},
//OR
"QRCode": {
//or simply define an object that contains the properties/methods
//that should be added to the proto of QRCode
//again configurable and writable but not enumerable
substr: String.prototype.substr,
substring: String.prototype.substring,
charAt: String.prototype.charAt,
charCodeAt: String.prototype.charCodeAt
}
});
//mixin-functions and objects can be mixed
यार, मैं मुसीबत ही बात कर रहे हो रही किया गया है। मैं उन्हें स्विफ्ट में प्यार करता हूं, लेकिन उन्हें जावास्क्रिप्ट में चाहिए! – boztalay