使用ES6在Javascript中枚举

Eri*_*Red 106 javascript enums symbols immutability ecmascript-6

我正在用Javascript重建一个旧的Java项目,并意识到在JS中没有好的方法来做枚举.

我能想到的最好的是:

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

const保持Colors被重新分配,并冷冻它防止突变的键和值.我正在使用符号,这Colors.RED是不等于0,或除了它自己之外的任何其他东西.

这个配方有问题吗?有没有更好的办法?


(我知道这个问题有点重复,但以前的Q/As都很老了,ES6给了我们一些新功能.)


编辑:

另一个解决序列化问题的解决方案,但我认为仍存在领域问题:

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

const Colors = Object.freeze({
    RED: enumValue("Colors.RED"),
    BLUE: enumValue("Colors.BLUE"),
    GREEN: enumValue("Colors.GREEN")
});
Run Code Online (Sandbox Code Playgroud)

通过使用对象引用作为值,可以获得与Symbols相同的冲突避免.

Ber*_*rgi 110

这个配方有问题吗?

我没有看到任何.

有没有更好的办法?

我将这两个陈述合并为一个:

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

如果您不喜欢样板,就像重复Symbol调用一样,您当然也可以编写一个帮助函数makeEnum,从名称列表中创建相同的东西.

  • 这里不存在领域问题吗? (3认同)
  • @torazaburo 你的意思是,当代码加载两次时,它会生成不同的符号,这对字符串来说不是问题?是的,很好,让它成为一个答案:-) (2认同)
  • @ErictheRed不,`Symbol.for`确实*没有*跨领域问题,但它确实与[真正的全局命名空间](/sf/answers/2168972011/)存在通常的碰撞问题. (2认同)
  • @Sky 在“Colors”中查找的默认值与枚举定义无关。人们可以像往常一样使用“Colors[name] ||”来做到这一点 Colors.BLUE` 或 `Colors.hasOwnProperty(name) ?颜色[名称]:颜色.BLUE`。 (2认同)
  • @ZachSmith,这样就可以在该语言的未来版本中赋予它含义,而不会破坏现有代码。 (2认同)

dsa*_*hez 17

更新 11.05.2020:
修改为包含静态字段和方法,以更接近地复制“真实”枚举行为。

如果您打算更新,我建议您尝试使用我所说的“枚举类”(除非您无法接受任何浏览器或运行时环境限制)。它基本上是一个非常简单和干净的类,它使用私有字段和有限的访问器来模拟枚举的行为。当我想在枚举中构建更多功能时,我有时会在 C# 中执行此操作。

我意识到私有类字段目前仍处于实验阶段,但它似乎可以用于创建具有不可变字段/属性的类。浏览器支持也不错。唯一不支持它的“主要”浏览器是 Firefox(我相信他们很快就会支持)和 IE(谁在乎)。

免责声明
我不是开发人员。我只是把这个放在一起来解决我在做个人项目时JS中不存在的枚举的限制。

样本班

class Colors {
    // Private Fields
    static #_RED = 0;
    static #_GREEN = 1;
    static #_BLUE = 2;

    // Accessors for "get" functions only (no "set" functions)
    static get RED() { return this.#_RED; }
    static get GREEN() { return this.#_GREEN; }
    static get BLUE() { return this.#_BLUE; }
}
Run Code Online (Sandbox Code Playgroud)

您现在应该可以直接调用您的枚举。

Colors.RED; // 0
Colors.GREEN; // 1
Colors.BLUE; // 2
Run Code Online (Sandbox Code Playgroud)

使用私有字段和有限访问器的组合意味着现有的枚举值受到很好的保护(它们现在基本上是常量)。

Colors.RED = 10 // Colors.RED is still 0
Colors._RED = 10 // Colors.RED is still 0
Colors.#_RED = 10 // Colors.RED is still 0
Run Code Online (Sandbox Code Playgroud)


Vas*_*şte 11

这是我个人的做法。

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);
Run Code Online (Sandbox Code Playgroud)

  • 我不建议使用它,因为它无法迭代所有可能的值,并且无法在不手动检查每个值的情况下检查值是否为 ColorType。 (4认同)

Jus*_*ery 9

虽然使用Symbolenum值可以很好地用于简单的用例,但是为枚举提供属性可能很方便.这可以通过使用Object包含属性的枚举值来完成.

例如,我们可以给出每个Colors名称和十六进制值:

/**
 * 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" }
});
Run Code Online (Sandbox Code Playgroud)

在枚举中包含属性可以避免必须编写switch语句(并且在扩展枚举时可能会忘记对switch语句的新案例).该示例还显示了使用JSDoc枚举注释记录的枚举属性和类型.

平等与Colors.RED === Colors.RED存在trueColors.RED === Colors.BLUE存在一样有效false.

  • 请注意,Object.freeze 并不深,例如您可以更改 Colors.RED.name = "charlie"; 您可以使用类似的方法来冻结嵌套对象: const deepFreeze = obj => { Object.keys(obj).forEach(prop => { if (typeof obj[prop] === 'object') deepFreeze(obj[prop ]); }); 返回 Object.freeze(obj); }; (5认同)

Vla*_*kyi 7

检查TypeScript是如何做到的.基本上他们做了以下事情:

const MAP = {};

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

MAP['A'] // 1
MAP[1] // A
Run Code Online (Sandbox Code Playgroud)

使用符号,冻结对象,无论你想要什么.


ton*_*har 7

如上所述,您还可以编写一个makeEnum()辅助函数:

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

像这样使用它:

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");
}
Run Code Online (Sandbox Code Playgroud)

  • 作为一行代码:`const makeEnum = (...lst) => Object.freeze(Object.assign({}, ...lst.map(k => ({[k]: Symbol(k)} ))));` 然后将其用作`const colors = makeEnum("Red", "Green", "Blue")` (2认同)

Chr*_*row 7

如果你不需要ES6 并且可以使用 Typescript,它有一个很好的enum

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


小智 5

您可以查看Enumify,这是一个非常出色且功能齐全的 ES6 枚举库。