Sunday 15 January 2012

Emulate Java Enums in JavaScript

In this recent post I described one technique to get Java like OOP enums in C#. It's time now to do the same in JavaScript.

As JavaScript is such an special language, the approach quite defers from the one I used in C#. Probably there are 1000 better ways to do this, but anyway, it seems enough for me, so I'll share here w what I've come up with. I've got a Enum "base class" from which all our enums will inherit. This base class contains the name property (the main value assigned to the enum) and the values "static" method (that for convenience we'll mixin to the derived Enum "classes"). We don't need a valueOf method cause we can just use the "[]" accessor.

//--------------  Enum "class" --------------------
//base class for all anums
var Enum = function(name){
 //add static methods to the derived class
 //enumerized is a flag, let's do the mixin only once
 if(!this.constructor.enumerized){
  Enum.doEnum(this.constructor);
  this.constructor.enumerized = true;
 }
 this.name = name;
 this.constructor._addEnumValue(this);
};

//mixins "static" methods to each enum "class"
//@type: the enum "class"(function)
Enum.doEnum = function(type){
 type.enumValues = [];
 //"static" methods, for convenience will mixin them to each enum class
 type.values = function(concreteEnum){
  //should return a copy of the array
  return this.enumValues;
 }
 /* Not needed, just use [name]
 type.valueOf = function(name){
 },
 */
 type._addEnumValue = function(obj){
  this.enumValues.push(obj);
 }
};
//----------------------------------------

//--------------  Planet "class" --------------------
var Planet = function _Planet(name, mass, radius){
 //invoke base constructor
 Enum.call(this, name);
 this.mass = mass;
 this.radius = radius;
};
Planet.G = 6.67;
Planet.prototype = new Enum();
Planet.prototype.constructor = Planet;
Planet.prototype.getSurfaceGravity = function(){
 return Planet.G * this.mass / this.radius * this.radius;
};
Planet.MERCURY = new Planet("MERCURY", 3, 2);
Planet.VENUS = new Planet("VENUS", 4, 6);
//----------------------------------------

//----------- testing ---------------
var mercury = Planet.MERCURY;
var venus = Planet["VENUS"];

print("MERCURY data:");
print(mercury.name + " " + mercury.getSurfaceGravity());

print("VENUS data:");
print(venus.name + " " + venus.getSurfaceGravity());

print("planets: ");
var planets = Planet.values();
for(var i=0; i<planets.length; i++){
 print(planets[i].name);
}

And the source

*Note: As you may have figured out, I'm putting in quotes "class" and "static" cause these are concepts that we can't purely apply to a prototype based language like JavaScript.

No comments:

Post a Comment