Enums in Javascript with ES6

2019-01-21 07:23发布

问题:

I'm rebuilding an old Java project in Javascript, and realized that there's no good way to do enums in JS.

The best I can come up with is:

const Colors = {
    RED: Symbol("red"),
    BLUE: Symbol("blue"),
    GREEN: Symbol("green")
};
Object.freeze(Colors);

The const keeps Colors from being reassigned, and freezing it prevents mutating the keys and values. I'm using Symbols so that Colors.RED is not equal to 0, or anything else besides itself.

Is there a problem with this formulation? Is there a better way?


(I know this question is a bit of a repeat, but all the previous Q/As are quite old, and ES6 gives us some new capabilities.)


EDIT:

Another solution, which deals with the serialization problem, but I believe still has realm issues:

const enumValue = (name) => Object.freeze({toString: () => name});

const Colors = Object.freeze({
    RED: enumValue("Colors.RED"),
    BLUE: enumValue("Colors.BLUE"),
    GREEN: enumValue("Colors.GREEN")
});

By using object references as the values, you get the same collision-avoidance as Symbols.

回答1:

Is there a problem with this formulation?

I don't see any.

Is there a better way?

I'd collapse the two statements into one:

const Colors = Object.freeze({
    RED:   Symbol("red"),
    BLUE:  Symbol("blue"),
    GREEN: Symbol("green")
});

If you don't like the boilerplate, like the repeated Symbol calls, you can of course also write a helper function makeEnum that creates the same thing from a list of names.



回答2:

Also check Enumify, a very good and well featured library for ES6 enums.

Hope this will help somebody.

Best regards,

Emmanuel



回答3:

Check how TypeScript does it. Basically they do the following:

const MAP = {};

MAP[MAP[1] = 'A'] = 1;
MAP[MAP[2] = 'B'] = 2;

MAP['A'] // 1
MAP[1] // A

Use symbols, freeze object, whatever you want.



回答4:

As mentioned above, you could also write a makeEnum() helper function:

function makeEnum(arr){
    let obj = {};
    for (let val of arr){
        obj[val] = Symbol(val);
    }
    return Object.freeze(obj);
}

Use it like this:

const Colors = makeEnum(["red","green","blue"]);
let startColor = Colors.red; 
console.log(startColor); // Symbol(red)

if(startColor == Colors.red){
    console.log("Do red things");
}else{
    console.log("Do non-red things");
}


回答5:

If you don't need pure ES6 and can use Typescript, it has a nice enum:

https://www.typescriptlang.org/docs/handbook/enums.html



回答6:

Maybe this solution ? :)

function createEnum (array) {
  return Object.freeze(array
    .reduce((obj, item) => {
      if (typeof item === 'string') {
        obj[item] = Symbol(item)
      }
      return obj
    }, {}))
}


回答7:

Whilst using Symbol as the enum value works fine for simple use cases, it can be handy to give properties to enums. This can be done by using an Object as the enum value containing the properties.

For example we can give each of the Colors a name and hex value:

/**
 * Enum for common colors.
 * @readonly
 * @enum {{name: string, hex: string}}
 */
const Colors = Object.freeze({
  RED:   { name: "red", hex: "#f00" },
  BLUE:  { name: "blue", hex: "#00f" },
  GREEN: { name: "green", hex: "#0f0" }
});

Including properties in the enum avoids having to write switch statements (and possibly forgetting new cases to the switch statements when an enum is extended). The example also shows the enum properties and types documented with the JSDoc enum annotation.

Equality works as expected with Colors.RED === Colors.RED being true, and Colors.RED === Colors.BLUE being false.



回答8:

This is my personal approach.

class ColorType {
    static get RED () {
        return "red";
    }

    static get GREEN () {
        return "green";
    }

    static get BLUE () {
        return "blue";
    }
}

// Use case.
const color = Color.create(ColorType.RED);


回答9:

you can also use es6-enum package (https://www.npmjs.com/package/es6-enum). It's very easy to use. See the example below



回答10:

You could use ES6 Map

const colors = new Map([
  ['RED', 'red'],
  ['BLUE', 'blue'],
  ['GREEN', 'green']
]);

console.log(colors.get('RED'));