2011-10-24

Enums for JavaScript

This post describes a JavaScript implementation of enums, enumerations of symbols. The idea originally comes from Allen Wirfs-Brock, via a thread on the es-discuss mailing list.

Using an enum

    var color = new enums.Enum("red", "green", "blue");
    function isGreen(c) {
        return c === color.green;
    }
color.green is an object (an instance of Symbol) and a === b is only true if a and b are the same object, so the above works out nicely. Interaction:
    > isGreen(color.red)
    false
    > isGreen(color.green)
    true
The cool thing is that switch works, too:
    function translate(c) {
        switch(c) {
            case color.red:
                return "rot";
            case color.green:
                return "grün";
            case color.blue:
                return "blau";
        }
    }
Interaction:
    > translate(color.red)
    'rot'
You can also give enum symbols custom properties:
    var color = new enums.Enum({
        red: { de: "rot" },
        green: { de: "grün" },
        blue: { de: "blau" },
    });
Then translate() becomes simpler:
    function translate(c) {
        return c.de;
    }
Lastly, enum symbols can also be converted to string:
    > console.log("The sky is "+color.blue);
    The sky is |blue|
    > console.log("The sky is "+color.blue.name);
    The sky is blue
Isn’t there an easier way? You might think that the following is simpler than an enum:
    var color2 = {
        red: "red",
        green: "green",
        blue: "blue",
    }
However, it is a problem of type safety: Enum symbols are not equal to anything but themselves. The properties of color2 do not exhibit this behavior:
    > color2.red === "red"
    true
    > color.red === "red"
    false

The implementation

You can download the enums project on GitHub. It consists of two constructors:
  • Symbol implements the symbols that an enum holds. Symbols are immutable.
  • Enum implements enums. Each enum is an object that maps symbol names to symbol instances. Enums are immutable.
There are several measures to make instances of Symbol as immutable as possible:
  • The prototype of symbols is frozen (immutable).
  • The prototype of symbols has no prototype. Normally, Symbol.prototype would have the prototype Object.prototype (which comes with several standard methods etc.). But Object.prototype is mutable which we want to avoid.
  • Instances of Symbol are frozen.
Note: Without Object.prototype in the prototype chain, we need to provide a toString() method. Otherwise symbols cannot be converted to string, e.g. to display them on a command line.

10 comments:

Daniel Steigerwald said...

I suppose this is good enough, or I missed something?
var color = {
RED: 'red',
GREEN: 'green'
BLUE: 'blue'
}

Jake Verbaten said...

You may want to present proper use cases for using your enum like the keys/values sugar methods you have and the fact that you enforce immutability on symbols.

Maybe some examples why its useful to have an immutable symbol rather then a normal object.

Axel Rauschmayer said...

I’ve added a paragraph that explains why enums are more complicated than that.

Axel Rauschmayer said...

“You may want to present proper use cases for using your enum like the
keys/values sugar methods you have and the fact that you enforce
immutability on symbols.”
Can you elaborate on what you mean and/or give examples?

“Maybe some examples why its useful to have an immutable symbol rather then a normal object.”
Good point, motivating immutability makes sense.

Jake Verbaten said...

 - proper use cases

I meant give examples why a simble object will not do but an enum will. Basically show the pitfalls an object has (identify the problem) and show how enums solve it (present the solution)

 - keys/values sugar

you have some methods on your enum that are defined in the source but not mentioned in your github README or in your blog post. I personally think there should be some source of documentation (even though the code is obvouis and well-written!)

 - enforcing immutability is a feature

It is but I can't explain why immutability is a good thing apart from "people claim it is". Immutability is a solution to a problem, I and others don't know much about that problem other then it exists.

Axel Rauschmayer said...

Good points. Will write more, once I have the time.

Daniel Steigerwald said...

I never ever needed to compare enum with anything else than enum. Sorry, your solution seems to be overengineering. Interesting, still now for daily usage.

Axel Rauschmayer said...

It’s not necessarily about comparing: If you get a parameter, how can you be sure that a value comes from an enum symbol and not from a random string? Enums just give you a little more safety with regard to legal values. Lastly, custom properties (including methods) for an enum symbol do help with many tasks. And you cannot have those on strings.

Robin Southgate said...

Removing the object prototype by using Object.create(null) doesn't seem to be supported in ie8... is there a way of getting that immutability in ie8?

Axel Rauschmayer said...

Keeping Object.prototype around shouldn’t be too bad. If the instances are frozen then there is not much that can go wrong (excluding malevolent code).

Web Analytics