使用 ES6 在 JavaScript 中枚举
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44447847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Enums in Javascript with ES6
提问by Eric the Red
I'm rebuilding an old Java project in Javascript, and realized that there's no good way to do enums in JS.
我正在用 Javascript 重建一个旧的 Java 项目,并意识到在 JS 中没有好的方法进行枚举。
The best I can come up with is:
我能想到的最好的是:
const Colors = {
RED: Symbol("red"),
BLUE: Symbol("blue"),
GREEN: Symbol("green")
};
Object.freeze(Colors);
The constkeeps Colorsfrom being reassigned, and freezing it prevents mutating the keys and values. I'm using Symbols so that Colors.REDis not equal to 0, or anything else besides itself.
的const保持Colors被重新分配,并冷冻它防止突变的键和值。我正在使用 Symbols,所以它Colors.RED不等于0,或者除了它本身之外的任何其他东西。
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/Asare quite old, and ES6 gives us some new capabilities.)
(我知道这个问题有点重复,但之前所有的Q/A都很老,而 ES6 给了我们一些新功能。)
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.
通过使用对象引用作为值,您可以获得与 Symbols 相同的碰撞避免。
采纳答案by Bergi
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 Symbolcalls, you can of course also write a helper function makeEnumthat creates the same thing from a list of names.
如果您不喜欢样板文件,例如重复Symbol调用,您当然也可以编写一个辅助函数makeEnum,从名称列表中创建相同的内容。
回答by Justin Emery
Whilst using Symbolas 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 Objectas the enum value containing the properties.
虽然使用Symbol作为枚举值对于简单的用例工作正常,但为枚举提供属性可能很方便。这可以通过使用 aObject作为包含属性的枚举值来完成。
For example we can give each of the Colorsa name and hex value:
例如,我们可以给每个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" }
});
Including properties in the enum avoids having to write switchstatements (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.
在枚举中包含属性可以避免编写switch语句(并且可能会在扩展枚举时忘记 switch 语句的新情况)。该示例还显示了使用JSDoc 枚举注释记录的枚举属性和类型。
Equality works as expected with Colors.RED === Colors.REDbeing true, and Colors.RED === Colors.BLUEbeing false.
Equality 与Colors.RED === Colors.REDbeingtrue和Colors.RED === Colors.BLUEbeing 的预期一致false。
回答by tonethar
As mentioned above, you could also write a makeEnum()helper function:
如上所述,您还可以编写一个makeEnum()辅助函数:
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");
}
回答by Vasile Alexandru Pe?te
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);
回答by givehug
Check how TypeScript does it. Basically they do the following:
检查TypeScript 是如何做到的。基本上他们执行以下操作:
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.
使用符号,冻结对象,随心所欲。
回答by Chris Halcrow
If you don't need pureES6 and can use Typescript, it has a nice enum:
如果你不需要纯ES6 并且可以使用 Typescript,它有一个很好的enum:
回答by Emmanuel.B
回答by Mateusz Stefański
Maybe this solution ? :)
也许这个解决方案?:)
function createEnum (array) {
return Object.freeze(array
.reduce((obj, item) => {
if (typeof item === 'string') {
obj[item.toUpperCase()] = Symbol(item)
}
return obj
}, {}))
}
Example:
例子:
createEnum(['red', 'green', 'blue']);
> {RED: Symbol(red), GREEN: Symbol(green), BLUE: Symbol(blue)}
回答by Fawaz
you can also use es6-enum package (https://www.npmjs.com/package/es6-enum). It's very easy to use. See the example below:
您还可以使用 es6-enum 包(https://www.npmjs.com/package/es6-enum)。它非常容易使用。请参阅下面的示例:
import Enum from "es6-enum";
const Colors = Enum("red", "blue", "green");
Colors.red; // Symbol(red)
回答by Cristian Malinescu
I prefer @tonethar's approach, with a little bit of enhancements and digging for the benefit of understanding better the underlyings of the ES6/Node.js ecosystem. With a background in the server side of the fence, I prefer the approach of functional style around platform's primitives, this minimizes the code bloat, the slippery slope into the state's management valley of the shadow of death due to the introduction of new types and increases the readability - makes more clear the intent of the solution and the algorithm.
我更喜欢 @tonethar 的方法,有一点点增强和挖掘,以便更好地理解 ES6/Node.js 生态系统的基础。有围栏服务器端的背景,我更喜欢围绕平台原语的功能风格的方法,这最大限度地减少了代码膨胀,由于新类型的引入而滑入死亡阴影的状态管理谷,并增加可读性 - 使解决方案和算法的意图更加清晰。
Solution with TDD, ES6, Node.js, Lodash, Jest, Babel, ESLint
使用TDD、ES6、Node.js、Lodash、Jest、Babel、ESLint 的解决方案
// ./utils.js
import _ from 'lodash';
const enumOf = (...args) =>
Object.freeze( Array.from( Object.assign(args) )
.filter( (item) => _.isString(item))
.map((item) => Object.freeze(Symbol.for(item))));
const sum = (a, b) => a + b;
export {enumOf, sum};
// ./utils.js
// ./kittens.js
import {enumOf} from "./utils";
const kittens = (()=> {
const Kittens = enumOf(null, undefined, 'max', 'joe', 13, -13, 'tabby', new
Date(), 'tom');
return () => Kittens;
})();
export default kittens();
// ./kittens.js
// ./utils.test.js
import _ from 'lodash';
import kittens from './kittens';
test('enum works as expected', () => {
kittens.forEach((kitten) => {
// in a typed world, do your type checks...
expect(_.isSymbol(kitten));
// no extraction of the wrapped string here ...
// toString is bound to the receiver's type
expect(kitten.toString().startsWith('Symbol(')).not.toBe(false);
expect(String(kitten).startsWith('Symbol(')).not.toBe(false);
expect(_.isFunction(Object.valueOf(kitten))).not.toBe(false);
const petGift = 0 === Math.random() % 2 ? kitten.description :
Symbol.keyFor(kitten);
expect(petGift.startsWith('Symbol(')).not.toBe(true);
console.log(`Unwrapped Christmas kitten pet gift '${petGift}', yeee :)
!!!`);
expect(()=> {kitten.description = 'fff';}).toThrow();
});
});
// ./utils.test.js

