JavaScriptで列挙型を定義するための推奨される構文は何ですか?何かのようなもの:
my.namespace.ColorEnum = {
RED : 0,
GREEN : 1,
BLUE : 2
}
// later on
if(currentColor == my.namespace.ColorEnum.RED) {
// whatever
}
それとももっと好ましいイディオムはありますか?
1.8.5以降、オブジェクトを封印してフリーズすることが可能ですので、上記を次のように定義します。
var DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})
または
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)
そしてよろしく! JS列挙型。
しかしながら、これはあなたが変数に望ましくない値を代入することを妨げるものではありません。それはしばしばenumの主な目的です:
let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors
より安全な型の安全性(enumなどを含む)を保証する1つの方法は、 TypeScript または Flow のようなツールを使用することです。
引用符は必要ではありませんが、私は一貫性のためにそれらを保ちました。
これはあまり答えではありませんが、私は個人的には問題なく動作していると思います
とは言っても、値が何であっても問題ないので(0、1、2を使ったことがあるので)、現在の値を出力したい場合に備えて意味のある文字列を使います。
_ update _ :すべての投票に感謝しますが、私の答えがJavascriptで列挙型を書くための最善の方法ではないと思います。詳細については私のブログ投稿を参照してください: JavascriptのEnums 。
名前を知らせることはすでに可能です。
if (currentColor == my.namespace.ColorEnum.RED) {
// alert name of currentColor (RED: 0)
var col = my.namespace.ColorEnum;
for (var name in col) {
if (col[name] == col.RED)
alert(name);
}
}
代わりに、値をオブジェクトにすることもできます。そのため、ケーキを持ってそれを食べることもできます。
var SIZE = {
SMALL : {value: 0, name: "Small", code: "S"},
MEDIUM: {value: 1, name: "Medium", code: "M"},
LARGE : {value: 2, name: "Large", code: "L"}
};
var currentSize = SIZE.MEDIUM;
if (currentSize == SIZE.MEDIUM) {
// this alerts: "1: Medium"
alert(currentSize.value + ": " + currentSize.name);
}
Javascriptでは、動的言語であるため、後でセットに列挙値を追加することさえ可能です。
// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};
Enumのフィールド(この例ではvalue、name、code)は、身元確認には必要ではなく、便宜上そこにしかありません。 sizeプロパティの名前自体もハードコードする必要はありませんが、動的に設定することもできます。新しいenum値の名前しかわからないと仮定しても、問題なく追加できます。
// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};
もちろん、これはいくつかの仮定ができなくなることを意味します(例えば、その値はサイズの正しい順序を表します)。
Javascriptでは、オブジェクトはマップまたはハッシュテーブルのようなものです。名前と値のペアのセット。事前に詳しく知らなくても、ループしたり他の方法で操作したりできます。
例えば:
for (var sz in SIZE) {
// sz will be the names of the objects in SIZE, so
// 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'
var size = SIZE[sz]; // Get the object mapped to the name in sz
for (var prop in size) {
// Get all the properties of the size object, iterates over
// 'value', 'name' and 'code'. You can inspect everything this way.
}
}
ところで、もしあなたがネームスペースに興味があるなら、javascriptのためのシンプルだが強力なネームスペースと依存関係管理のための私の解決策を見たいと思うかもしれません: パッケージJS
結論:できません。
あなたはそれを偽造することができますが、あなたはタイプセーフを得ることはできません。通常、これは整数値にマップされた文字列値の単純な辞書を作成することによって行われます。例えば:
var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Document.Write("Enumerant: " + DaysEnum.tuesday);
このアプローチの問題?誤って列挙型を再定義したり、誤って列挙型の値が重複したりする可能性があります。例えば:
DaysEnum.monday = 4; // whoops, monday is now thursday, too
編集
Artur CzajkaのObject.freezeはどうですか?それは月曜日から木曜日にあなたが設定することを妨げるために働きませんか? - フライクワッド
絶対に、 Object.freeze
は私が不満を言っていた問題を完全に解決するでしょう。私が上記を書いたとき、Object.freeze
が本当に存在しなかったことを皆に思い出させたいです。
さて、これでいくつかのとても面白い可能性が開かれました。
編集2
これはenumを作成するためのとても良いライブラリです。
http://www.2ality.com/2011/10/enums.html
列挙型のすべての有効な使用法にはおそらく当てはまりませんが、非常に長い道のりがあります。
これが私たち全員が欲しいものです:
function Enum(constantsList) {
for (var i in constantsList) {
this[constantsList[i]] = i;
}
}
今、あなたはあなたの列挙型を作成することができます:
var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);
こうすることで、定数は通常の方法でアクセスされ(YesNo.YES、Color.GREEN)、順次int値(NO = 0、YES = 1、RED = 0、GREEN = 1、BLUE = 2)を取得します。
Enum.prototypeを使ってメソッドを追加することもできます。
Enum.prototype.values = function() {
return this.allValues;
/* for the above to work, you'd need to do
this.allValues = constantsList at the constructor */
};
編集 - ちょっとした改善 - 今では可変引数付き:(残念ながらIE:Sでは正しく動作しません。その場合は以前のバージョンをそのまま使用する必要があります)
function Enum() {
for (var i in arguments) {
this[arguments[i]] = i;
}
}
var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');
最近のほとんどのブラウザでは、列挙型の作成に使用できる symbol primitiveデータ型があります。各シンボル値はJavaScriptによって一意、つまりSymbol() != Symbol()
であることが保証されているため、enumの型安全性が保証されます。例えば:
const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});
デバッグを簡単にするために、列挙値に説明を追加できます。
const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});
GitHub enumを初期化するのに必要なコードを単純化するラッパーを見つけることができます:
const color = new Enum("RED", "BLUE")
color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE
私は自分のenumが大好きなので、これで遊んできました。 =)
Object.defineProperty
を使用する私はやや実行可能な解決策を思いついたと思います。
これがjsfiddleです: http://jsfiddle.net/ZV4A6/ /
このメソッドを使用すると、(理論上)そのオブジェクトの他の属性に影響を与えることなく、任意のオブジェクトのenum値を呼び出して定義できるようになります。
Object.defineProperty(Object.prototype,'Enum', {
value: function() {
for(i in arguments) {
Object.defineProperty(this,arguments[i], {
value:parseInt(i),
writable:false,
enumerable:true,
configurable:true
});
}
return this;
},
writable:false,
enumerable:false,
configurable:false
});
属性writable:false
のためにthis すべき それをタイプセーフにしなさい。
そのため、カスタムオブジェクトを作成して、それにEnum()
を呼び出すことができるはずです。割り当てられる値は0から始まり、項目ごとに増加します。
var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED; // == 0
EnumColors.BLUE; // == 1
EnumColors.GREEN; // == 2
EnumColors.YELLOW; // == 3
これは私が知っている古いものですが、それ以来TypeScriptインターフェースを介して実装されてきた方法は次のとおりです。
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["Foo"] = 0] = "Foo";
MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));
これにより、宣言の順序に関係なく、1を返すMyEnum.Bar
と "Bar"を返すMyEnum[1]
の両方を調べることができます。
ほとんどの人による「好ましい構文」はすでに上にリストされています。ただし、パフォーマンスという大きな包括的な問題があります。上記の答えの1つだけでも、ほんの少しの場合でも非常に優れたパフォーマンスを発揮するものではありません。それらすべてが、コードサイズを極端に大きくすることになります。実際のパフォーマンス、コードの読みやすさ、および縮小によるコードサイズの前例のない縮小のために、これは列挙を行うための正しい方法です。
const ENUM_COLORENUM_RED = 0,
ENUM_COLORENUM_GREEN = 1,
ENUM_COLORENUM_BLUE = 2,
ENUMLEN_COLORENUM = 3;
// later on
if(currentColor == ENUM_COLORENUM_RED) {
// whatever
}
さらに、この構文は、以下に示すように明確で簡潔なクラス拡張を可能にします。
(長さ:2,450バイト)
(function(window){
"use strict";
var parseInt = window.parseInt
const ENUM_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
ENUMLEN_PIXELCOLOR = 1,
ENUM_SOLIDCOLOR_R = ENUMLEN_PIXELCOLOR+0,
ENUM_SOLIDCOLOR_G = ENUMLEN_PIXELCOLOR+1,
ENUM_SOLIDCOLOR_B = ENUMLEN_PIXELCOLOR+2,
ENUMLEN_SOLIDCOLOR = ENUMLEN_PIXELCOLOR+3,
ENUM_ALPHACOLOR_R = ENUMLEN_PIXELCOLOR+0,
ENUM_ALPHACOLOR_G = ENUMLEN_PIXELCOLOR+1,
ENUM_ALPHACOLOR_B = ENUMLEN_PIXELCOLOR+2,
ENUM_ALPHACOLOR_A = ENUMLEN_PIXELCOLOR+3,
ENUMLEN_ALPHACOLOR = ENUMLEN_PIXELCOLOR+4,
ENUM_PIXELTYPE_SOLID = 0,
ENUM_PIXELTYPE_ALPHA = 1,
ENUM_PIXELTYPE_UNKNOWN = 2,
ENUMLEN_PIXELTYPE = 2;
function parseHexColor(rawstr) {
rawstr = rawstr.trim().substring(1);
var result = [];
if (rawstr.length === 8) {
result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
} else if (rawstr.length === 4) {
result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
} else if (rawstr.length === 6) {
result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
} else if (rawstr.length === 3) {
result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
} else {
result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
}
return result;
}
// the red component of green
console.log(parseHexColor("#0f0")[ENUM_SOLIDCOLOR_R]);
// the alpha of transparent purple
console.log(parseHexColor("#f0f7")[ENUM_ALPHACOLOR_A]);
// the enumerated array for turquoise
console.log(parseHexColor("#40E0D0"));
})(self);
これは他の解決策よりも実用的ではないと言う人もいるかもしれません:それはスペースのトンがたくさんある、それは書くのに長い時間がかかり、そしてそれは砂糖の構文で覆われていません。はい、それらのコードはコードを縮小しなければ正しいでしょう。しかし、合理的な人が最終製品に未確認のコードを残すことはありません。この縮小のために、Closure Compilerは私がまだ見つけていない最高のものです。オンラインアクセスは ここ で見つけることができます。クロージャコンパイラは、この列挙型データをすべて取り込んでインライン化することができます。これにより、JavaScriptの実行速度が非常に速くなり、非常に小さくなります。観察する。
(長さ:605バイト)
'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);
それでは、これらの列挙がないと、同等のファイルがどれだけ大きくなるかを見てみましょう。
これらの列挙のないソース (長さ:1,973バイト(477バイトより短い!))
これらの列挙なしで縮小 (長さ:843バイト(238バイト より長い ))
見られるように、列挙なしで、より大きな縮小コードを犠牲にしてソースコードはより短くなります。私はあなたのことを知りません、私はソースコードを最終製品に組み入れたくないと確信しています、それが結果としてファイルサイズを小さくするという点ではるかに優れたものにします。それに加えて、この形式の列挙はずっと高速です。確かに、この列挙型の列挙は進むべき道です。
ES7 では、静的属性に頼ってエレガントなENUMを実行できます。
class ColorEnum {
static RED = 0 ;
static GREEN = 1;
static BLUE = 2;
}
それから
if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}
(リテラルオブジェクトの代わりにクラスを使うことの)利点は、親クラスEnum
を持つことです。そうすればあなたのすべてのEnumは extends そのクラスになります。
class ColorEnum extends Enum {/*....*/}
TLDR: このクラスをユーティリティメソッドに追加してコード全体で使用すると、従来のプログラミング言語のEnumの動作が模倣され、実際には存在しない列挙子にアクセスしようとするとエラーが発生します。列挙子を更新します。 Object.freeze()
に頼る必要はありません。
class Enum {
constructor(enumObj) {
const handler = {
get(target, name) {
if (typeof target[name] != 'undefined') {
return target[name];
}
throw new Error(`No such enumerator: ${name}`);
},
set() {
throw new Error('Cannot add/update properties on an Enum instance after it is defined')
}
};
return new Proxy(enumObj, handler);
}
}
次に、クラスをインスタンス化して列挙型を作成します。
const roles = new Enum({
ADMIN: 'Admin',
USER: 'User',
});
フル説明:
伝統的な言語から得られるEnumsの非常に有益な特徴の1つは、存在しない列挙子にアクセスしようとするとそれらが爆発する(コンパイル時エラーをスローする)ことです。
誤った/悪意を持って追加された値が追加されるのを防ぐためにモックされたenum構造を凍結することに加えて、他の答えのどれもEnumの本質的な機能に対処していません。
ご存じのとおり、JavaScriptで存在しないメンバーにアクセスしても単にundefined
が返されるだけで、コードが破壊されることはありません。列挙子は事前に定義された定数(つまり、曜日)であるため、列挙子を未定義にするべきではありません。
誤解しないでください。未定義のプロパティにアクセスしたときにundefined
を返すというJavaScriptの動作は、実際には非常に強力な言語の機能ですが、従来のEnum構造体をモックしようとしているときに必要な機能ではありません。
これがProxyオブジェクトが輝くところです。プロキシはES6(ES2015)の導入により言語で標準化されました。これがMDNからの説明です。
Proxyオブジェクトは、基本操作(例えば、プロパティ検索、割り当て、列挙、関数[....]呼び出しなど)に対するカスタム動作を定義するために使用される。
Webサーバープロキシと同様に、JavaScriptプロキシはオブジェクトに対する操作を傍受し(「トラップ」を使用し、必要に応じてそれらをフックする)、完了する前にさまざまなチェック、アクション、および/または操作を実行できます。場合によっては、操作を完全に停止することもあります。これは、存在しない列挙子を参照しようとしたときに、まさに私たちがやりたいことです。
これは、Enumを模倣するためにProxyオブジェクトを使用する人為的な例です。この例の列挙子は標準のHTTPメソッド(すなわち、 "GET"、 "POST"など)です。
// Class for creating enums (13 lines)
// Feel free to add this to your utility library in
// your codebase and profit! Note: As Proxies are an ES6
// feature, some browsers/clients may not support it and
// you may need to transpile using a service like babel
class Enum {
// The Enum class instantiates a JavaScript Proxy object.
// Instantiating a `Proxy` object requires two parameters,
// a `target` object and a `handler`. We first define the handler,
// then use the handler to instantiate a Proxy.
// A proxy handler is simply an object whose properties
// are functions which define the behavior of the proxy
// when an operation is performed on it.
// For enums, we need to define behavior that lets us check what enumerator
// is being accessed and what enumerator is being set. This can be done by
// defining "get" and "set" traps.
constructor(enumObj) {
const handler = {
get(target, name) {
if (typeof target[name] != 'undefined') {
return target[name]
}
throw new Error(`No such enumerator: ${name}`)
},
set() {
throw new Error('Cannot add/update properties on an Enum instance after it is defined')
}
}
// Freeze the target object to prevent modifications
return new Proxy(enumObj, handler)
}
}
// Now that we have a generic way of creating Enums, lets create our first Enum!
const httpMethods = new Enum({
DELETE: "DELETE",
GET: "GET",
OPTIONS: "OPTIONS",
PATCH: "PATCH",
POST: "POST",
PUT: "PUT"
})
// Sanity checks
console.log(httpMethods.DELETE)
// logs "DELETE"
try {
httpMethods.delete = "delete"
} catch (e) {
console.log("Error: ", e.message)
}
// throws "Cannot add/update properties on an Enum instance after it is defined"
try {
console.log(httpMethods.delete)
} catch (e) {
console.log("Error: ", e.message)
}
// throws "No such enumerator: delete"
ASIDE:一体何がプロキシなのですか?
私が最初に至る所でWordプロキシを見始めたときのことを覚えていますが、長い間私には意味がありませんでした。それが今のところ、プロキシを一般化する簡単な方法は、それらをソフトウェア、機関、あるいは2つのサーバー、企業、または人々の仲介者または仲介者として働く人々と考えることです。
これが私が使う解決策です。
function Enum() {
this._enums = [];
this._lookups = {};
}
Enum.prototype.getEnums = function() {
return _enums;
}
Enum.prototype.forEach = function(callback){
var length = this._enums.length;
for (var i = 0; i < length; ++i){
callback(this._enums[i]);
}
}
Enum.prototype.addEnum = function(e) {
this._enums.Push(e);
}
Enum.prototype.getByName = function(name) {
return this[name];
}
Enum.prototype.getByValue = function(field, value) {
var lookup = this._lookups[field];
if(lookup) {
return lookup[value];
} else {
this._lookups[field] = ( lookup = {});
var k = this._enums.length - 1;
for(; k >= 0; --k) {
var m = this._enums[k];
var j = m[field];
lookup[j] = m;
if(j == value) {
return m;
}
}
}
return null;
}
function defineEnum(definition) {
var k;
var e = new Enum();
for(k in definition) {
var j = definition[k];
e[k] = j;
e.addEnum(j)
}
return e;
}
そして、あなたはあなたの列挙型を次のように定義します。
var COLORS = defineEnum({
RED : {
value : 1,
string : 'red'
},
GREEN : {
value : 2,
string : 'green'
},
BLUE : {
value : 3,
string : 'blue'
}
});
そして、これはあなたがあなたのenumにアクセスする方法です:
COLORS.BLUE.string
COLORS.BLUE.value
COLORS.getByName('BLUE').string
COLORS.getByValue('value', 1).string
COLORS.forEach(function(e){
// do what you want with e
});
私は通常、メッセージオブジェクトからenumをマッピングするために最後の2つの方法を使います。
このアプローチのいくつかの利点:
いくつかの欠点:
オブジェクトリテラルを作成します。
const Modes = {
DRAGGING: 'drag',
SCALING: 'scale',
CLICKED: 'click'
};
Backbone を使用している場合は、 Backbone.Collection を使用して無料で本格的な列挙型機能(ID、名前、カスタムメンバーで検索)を取得できます。
// enum instance members, optional
var Color = Backbone.Model.extend({
print : function() {
console.log("I am " + this.get("name"))
}
});
// enum creation
var Colors = new Backbone.Collection([
{ id : 1, name : "Red", rgb : 0xFF0000},
{ id : 2, name : "Green" , rgb : 0x00FF00},
{ id : 3, name : "Blue" , rgb : 0x0000FF}
], {
model : Color
});
// Expose members through public fields.
Colors.each(function(color) {
Colors[color.get("name")] = color;
});
// using
Colors.Red.print()
あなたの答えはあまりにも複雑すぎる
var buildSet = function(array) {
var set = {};
for (var i in array) {
var item = array[i];
set[item] = item;
}
return set;
}
var myEnum = buildSet(['RED','GREEN','BLUE']);
// myEnum.RED == 'RED' ...etc
私はAndre 'Fi'の解決法を修正しました:
function Enum() {
var that = this;
for (var i in arguments) {
that[arguments[i]] = i;
}
this.name = function(value) {
for (var key in that) {
if (that[key] == value) {
return key;
}
}
};
this.exist = function(value) {
return (typeof that.name(value) !== "undefined");
};
if (Object.freeze) {
Object.freeze(that);
}
}
テスト:
var Color = new Enum('RED', 'GREEN', 'BLUE');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true
IE8はfreeze()メソッドをサポートしていません。
ソース: http://kangax.github.io/compat-table/es5/ 、[古いブラウザを表示しますか?]をクリックします。上に、そしてIE8をチェックして&col列交差点を凍結してください。
私の現在のゲームプロジェクトでは、まだIE8を使用しているユーザーはほとんどいないので、以下で使用しました。
var CONST_WILD_TYPES = {
REGULAR: 'REGULAR',
EXPANDING: 'EXPANDING',
STICKY: 'STICKY',
SHIFTING: 'SHIFTING'
};
我々はまたすることができます:
var CONST_WILD_TYPES = {
REGULAR: 'RE',
EXPANDING: 'EX',
STICKY: 'ST',
SHIFTING: 'SH'
};
あるいはこれさえ:
var CONST_WILD_TYPES = {
REGULAR: '1',
EXPANDING: '2',
STICKY: '3',
SHIFTING: '4'
};
最後のものは、文字列に対して最も効率的であるように思われます、あなたがサーバーとクライアントがこのデータを交換しているならば、それはあなたの総帯域幅を減らします。
もちろん、データに競合がないことを確認するのはあなたの義務です(RE、EXなどは一意でなければならず、1、2なども一意である必要があります)。後方互換性のためにこれらを永遠に維持する必要があることに注意してください。
割り当て:
var wildType = CONST_WILD_TYPES.REGULAR;
比較:
if (wildType === CONST_WILD_TYPES.REGULAR) {
// do something here
}
var ColorEnum = {
red: {},
green: {},
blue: {}
}
このように、異なるenum値に重複した番号を割り当てないようにする必要はありません。新しいオブジェクトがインスタンス化され、すべてのenum値に割り当てられます。
私は this というアプローチを思いついた。これはJavaのenumをモデルにしている。これらは型保証されているので、instanceof
チェックも実行できます。
あなたはこのような列挙型を定義することができます:
var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);
Days
はDays
列挙型を参照するようになりました。
Days.Monday instanceof Days; // true
Days.Friday.name(); // "Friday"
Days.Friday.ordinal(); // 4
Days.Sunday === Days.Sunday; // true
Days.Sunday === Days.Friday; // false
Days.Sunday.toString(); // "Sunday"
Days.toString() // "Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } "
Days.values().map(function(e) { return e.name(); }); //["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
Days.values()[4].name(); //"Friday"
Days.fromName("Thursday") === Days.Thursday // true
Days.fromName("Wednesday").name() // "Wednesday"
Days.Friday.fromName("Saturday").name() // "Saturday"
実装:
var Enum = (function () {
/**
* Function to define an enum
* @param typeName - The name of the enum.
* @param constants - The constants on the enum. Can be an array of strings, or an object where each key is an enum
* constant, and the values are objects that describe attributes that can be attached to the associated constant.
*/
function define(typeName, constants) {
/** Check Arguments **/
if (typeof typeName === "undefined") {
throw new TypeError("A name is required.");
}
if (!(constants instanceof Array) && (Object.getPrototypeOf(constants) !== Object.prototype)) {
throw new TypeError("The constants parameter must either be an array or an object.");
} else if ((constants instanceof Array) && constants.length === 0) {
throw new TypeError("Need to provide at least one constant.");
} else if ((constants instanceof Array) && !constants.reduce(function (isString, element) {
return isString && (typeof element === "string");
}, true)) {
throw new TypeError("One or more elements in the constant array is not a string.");
} else if (Object.getPrototypeOf(constants) === Object.prototype && !Object.keys(constants).reduce(function (isObject, constant) {
return Object.getPrototypeOf(constants[constant]) === Object.prototype;
}, true)) {
throw new TypeError("One or more constants do not have an associated object-value.");
}
var isArray = (constants instanceof Array);
var isObject = !isArray;
/** Private sentinel-object used to guard enum constructor so that no one else can create enum instances **/
function __() { };
/** Dynamically define a function with the same name as the enum we want to define. **/
var __enum = new Function(["__"],
"return function " + typeName + "(sentinel, name, ordinal) {" +
"if(!(sentinel instanceof __)) {" +
"throw new TypeError(\"Cannot instantiate an instance of " + typeName + ".\");" +
"}" +
"this.__name = name;" +
"this.__ordinal = ordinal;" +
"}"
)(__);
/** Private objects used to maintain enum instances for values(), and to look up enum instances for fromName() **/
var __values = [];
var __dict = {};
/** Attach values() and fromName() methods to the class itself (kind of like static methods). **/
Object.defineProperty(__enum, "values", {
value: function () {
return __values;
}
});
Object.defineProperty(__enum, "fromName", {
value: function (name) {
var __constant = __dict[name]
if (__constant) {
return __constant;
} else {
throw new TypeError(typeName + " does not have a constant with name " + name + ".");
}
}
});
/**
* The following methods are available to all instances of the enum. values() and fromName() need to be
* available to each constant, and so we will attach them on the prototype. But really, they're just
* aliases to their counterparts on the prototype.
*/
Object.defineProperty(__enum.prototype, "values", {
value: __enum.values
});
Object.defineProperty(__enum.prototype, "fromName", {
value: __enum.fromName
});
Object.defineProperty(__enum.prototype, "name", {
value: function () {
return this.__name;
}
});
Object.defineProperty(__enum.prototype, "ordinal", {
value: function () {
return this.__ordinal;
}
});
Object.defineProperty(__enum.prototype, "valueOf", {
value: function () {
return this.__name;
}
});
Object.defineProperty(__enum.prototype, "toString", {
value: function () {
return this.__name;
}
});
/**
* If constants was an array, we can the element values directly. Otherwise, we will have to use the keys
* from the constants object.
*/
var _constants = constants;
if (isObject) {
_constants = Object.keys(constants);
}
/** Iterate over all constants, create an instance of our enum for each one, and attach it to the enum type **/
_constants.forEach(function (name, ordinal) {
// Create an instance of the enum
var __constant = new __enum(new __(), name, ordinal);
// If constants was an object, we want to attach the provided attributes to the instance.
if (isObject) {
Object.keys(constants[name]).forEach(function (attr) {
Object.defineProperty(__constant, attr, {
value: constants[name][attr]
});
});
}
// Freeze the instance so that it cannot be modified.
Object.freeze(__constant);
// Attach the instance using the provided name to the enum type itself.
Object.defineProperty(__enum, name, {
value: __constant
});
// Update our private objects
__values.Push(__constant);
__dict[name] = __constant;
});
/** Define a friendly toString method for the enum **/
var string = typeName + " { " + __enum.values().map(function (c) {
return c.name();
}).join(", ") + " } ";
Object.defineProperty(__enum, "toString", {
value: function () {
return string;
}
});
/** Freeze our private objects **/
Object.freeze(__values);
Object.freeze(__dict);
/** Freeze the prototype on the enum and the enum itself **/
Object.freeze(__enum.prototype);
Object.freeze(__enum);
/** Return the enum **/
return __enum;
}
return {
define: define
}
})();
NPMパッケージを公開しました gen_enum JavascriptでEnumデータ構造をすばやく作成できます:
var genEnum = require('gen_enum');
var AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD');
var curMode = AppMode.LOG_IN;
console.log(curMode.isLogIn()); // output true
console.log(curMode.isSignUp()); // output false
console.log(curMode.isForgotPassword()); // output false
この小さなツールの優れた点の1つは、近代的な環境(nodejsおよびIE 9+ブラウザーを含む)で、返されるEnumオブジェクトが不変であることです。
詳細については、チェックアウトしてください https://github.com/greenlaw110/enumjs
更新
gen_enum
パッケージを廃止し、関数を constjs パッケージにマージします。これにより、不変オブジェクト、JSON文字列の逆シリアル化、文字列定数、ビットマップ生成などの機能が追加されます。チェックアウト https:/ /www.npmjs.com/package/constjs 詳細については
gen_enum
からconstjs
にアップグレードするには、ステートメントを変更するだけです
var genEnum = require('gen_enum');
に
var genEnum = require('constjs').enum;
O(1)で値と名前を取得できるEnumクラスを作りました。すべての名前と値を含むオブジェクト配列も生成できます。
function Enum(obj) {
// Names must be unique, Values do not.
// Putting same values for different Names is risky for this implementation
this._reserved = {
_namesObj: {},
_objArr: [],
_namesArr: [],
_valuesArr: [],
_selectOptionsHTML: ""
};
for (k in obj) {
if (obj.hasOwnProperty(k)) {
this[k] = obj[k];
this._reserved._namesObj[obj[k]] = k;
}
}
}
(function () {
this.GetName = function (val) {
if (typeof this._reserved._namesObj[val] === "undefined")
return null;
return this._reserved._namesObj[val];
};
this.GetValue = function (name) {
if (typeof this[name] === "undefined")
return null;
return this[name];
};
this.GetObjArr = function () {
if (this._reserved._objArr.length == 0) {
var arr = [];
for (k in this) {
if (this.hasOwnProperty(k))
if (k != "_reserved")
arr.Push({
Name: k,
Value: this[k]
});
}
this._reserved._objArr = arr;
}
return this._reserved._objArr;
};
this.GetNamesArr = function () {
if (this._reserved._namesArr.length == 0) {
var arr = [];
for (k in this) {
if (this.hasOwnProperty(k))
if (k != "_reserved")
arr.Push(k);
}
this._reserved._namesArr = arr;
}
return this._reserved._namesArr;
};
this.GetValuesArr = function () {
if (this._reserved._valuesArr.length == 0) {
var arr = [];
for (k in this) {
if (this.hasOwnProperty(k))
if (k != "_reserved")
arr.Push(this[k]);
}
this._reserved._valuesArr = arr;
}
return this._reserved._valuesArr;
};
this.GetSelectOptionsHTML = function () {
if (this._reserved._selectOptionsHTML.length == 0) {
var html = "";
for (k in this) {
if (this.hasOwnProperty(k))
if (k != "_reserved")
html += "<option value='" + this[k] + "'>" + k + "</option>";
}
this._reserved._selectOptionsHTML = html;
}
return this._reserved._selectOptionsHTML;
};
}).call(Enum.prototype);
このように初期化することができます。
var enum1 = new Enum({
item1: 0,
item2: 1,
item3: 2
});
値を取得するには(C#のEnumのように):
var val2 = enum1.item2;
値の名前を取得するには(異なる名前に同じ値を設定すると曖昧になる可能性があります)。
var name1 = enum1.GetName(0); // "item1"
オブジェクト内のそれぞれの名前と値を持つ配列を取得するには
var arr = enum1.GetObjArr();
生成されます:
[{ Name: "item1", Value: 0}, { ... }, ... ]
また、HTMLのselectオプションをすぐに入手できます。
var html = enum1.GetSelectOptionsHTML();
どれが成り立つ:
"<option value='0'>item1</option>..."
あなたはこのようなことをすることができます
var Enum = (function(foo) {
var EnumItem = function(item){
if(typeof item == "string"){
this.name = item;
} else {
this.name = item.name;
}
}
EnumItem.prototype = new String("DEFAULT");
EnumItem.prototype.toString = function(){
return this.name;
}
EnumItem.prototype.equals = function(item){
if(typeof item == "string"){
return this.name == item;
} else {
return this == item && this.name == item.name;
}
}
function Enum() {
this.add.apply(this, arguments);
Object.freeze(this);
}
Enum.prototype.add = function() {
for (var i in arguments) {
var enumItem = new EnumItem(arguments[i]);
this[enumItem.name] = enumItem;
}
};
Enum.prototype.toList = function() {
return Object.keys(this);
};
foo.Enum = Enum;
return Enum;
})(this);
var STATUS = new Enum("CLOSED","PENDING", { name : "CONFIRMED", ackd : true });
var STATE = new Enum("CLOSED","PENDING","CONFIRMED",{ name : "STARTED"},{ name : "PROCESSING"});
このライブラリで定義されているとおり。 https://github.com/webmodule/foo/blob/master/foo.js#L217
完全な例 https://Gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026
ES2015では 静的メソッドのみ (静的プロパティではない)がサポートされていますが( here 、§15.2.2.2も参照)、es2015
プリセットを付けたBabelでは以下を使用できます。
class CellState {
v: string;
constructor(v: string) {
this.v = v;
Object.freeze(this);
}
static EMPTY = new CellState('e');
static OCCUPIED = new CellState('o');
static HIGHLIGHTED = new CellState('h');
static values = function(): Array<CellState> {
const rv = [];
rv.Push(CellState.EMPTY);
rv.Push(CellState.OCCUPIED);
rv.Push(CellState.HIGHLIGHTED);
return rv;
}
}
Object.freeze(CellState);
他のモジュールからCellState
列挙型をインポートするなど、モジュール間でも期待通りに動いていることがわかりました。また、Webpackを使ってモジュールをインポートした場合も同様です。
このメソッドが他のほとんどの答えよりも優れている点は、静的型チェッカー (例: Flow )と一緒に使用でき、開発時に静的型チェックを使用して変数を宣言できることです。 、パラメータなどは、他の列挙型ではなく特定のCellState
"enum"です(一般的なオブジェクトやシンボルを使用した場合は区別ができません)。
上記のコードには、CellState
型の追加のオブジェクトを作成できるという点で欠点があります(ただし、凍結されているためCellState
の静的フィールドに割り当てることはできません)。それでも、以下のより洗練されたコードは以下の利点を提供します。
CellState
型のオブジェクトはこれ以上作成できませんenumのすべてのインスタンスを返すvalues
関数は、上記の手動の(そしてエラーが発生しやすい)方法で戻り値を作成する必要はありません。
'use strict';
class Status {
constructor(code, displayName = code) {
if (Status.INSTANCES.has(code))
throw new Error(`duplicate code value: [${code}]`);
if (!Status.canCreateMoreInstances)
throw new Error(`attempt to call constructor(${code}`+
`, ${displayName}) after all static instances have been created`);
this.code = code;
this.displayName = displayName;
Object.freeze(this);
Status.INSTANCES.set(this.code, this);
}
toString() {
return `[code: ${this.code}, displayName: ${this.displayName}]`;
}
static INSTANCES = new Map();
static canCreateMoreInstances = true;
// the values:
static ARCHIVED = new Status('Archived');
static OBSERVED = new Status('Observed');
static SCHEDULED = new Status('Scheduled');
static UNOBSERVED = new Status('Unobserved');
static UNTRIGGERED = new Status('Untriggered');
static values = function() {
return Array.from(Status.INSTANCES.values());
}
static fromCode(code) {
if (!Status.INSTANCES.has(code))
throw new Error(`unknown code: ${code}`);
else
return Status.INSTANCES.get(code);
}
}
Status.canCreateMoreInstances = false;
Object.freeze(Status);
exports.Status = Status;
執筆の時点で、 2014年10月 - だからここに現代的な解決策があります。ソリューションをノードモジュールとして書いていて、MochaとChaiを使ったテストとアンダースコアJSを含んでいます。あなたは簡単にこれらを無視することができます、そして望むならただEnumコードを使ってください。
過度に複雑なライブラリなどでたくさんの投稿を見ました。Javascriptでenumサポートを得るための解決策はとても単純です、それは本当に必要ではありません。これがコードです:
ファイル:enums.js
_ = require('underscore');
var _Enum = function () {
var keys = _.map(arguments, function (value) {
return value;
});
var self = {
keys: keys
};
for (var i = 0; i < arguments.length; i++) {
self[keys[i]] = i;
}
return self;
};
var fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));
var encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));
exports.fileFormatEnum = fileFormatEnum;
exports.encodingEnum = encodingEnum;
そしてそれがあなたに与えるものを説明するためのテスト:
ファイル:enumsSpec.js
var chai = require("chai"),
assert = chai.assert,
expect = chai.expect,
should = chai.should(),
enums = require('./enums'),
_ = require('underscore');
describe('enums', function () {
describe('fileFormatEnum', function () {
it('should return expected fileFormat enum declarations', function () {
var fileFormatEnum = enums.fileFormatEnum;
should.exist(fileFormatEnum);
assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');
assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');
});
});
describe('encodingEnum', function () {
it('should return expected encoding enum declarations', function () {
var encodingEnum = enums.encodingEnum;
should.exist(encodingEnum);
assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format');
assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');
});
});
});
ご覧のとおり、Enumファクトリを取得し、enum.keysを呼び出すだけですべてのキーを取得できます。また、キー自体を整数定数に対応付けることもできます。そして、ファクトリーをさまざまな値で再利用したり、Nodeのモジュラーアプローチを使用してそれらの生成されたEnumをエクスポートすることができます。
繰り返しになりますが、あなたがただの普通のユーザーではない、あるいはブラウザなどにいるのであれば、コードのファクトリの一部を取ってください。
すばやく簡単な方法は次のとおりです。
var Colors = function(){
return {
'WHITE':0,
'BLACK':1,
'RED':2,
'GREEN':3
}
}();
console.log(Colors.WHITE) //this prints out "0"
TypeScript enum を実装するには、2つの方法があります。
最も簡単な方法は、反転したキーと値のペアをオブジェクトに追加して、オブジェクトを反復処理することです。唯一の欠点は、各メンバーの値を手動で設定しなければならないことです。
function _enum(list) {
for (var key in list) {
list[list[key] = list[key]] = key;
}
return Object.freeze(list);
}
var Color = _enum({
Red: 0,
Green: 5,
Blue: 2
});
// Color → {0: "Red", 2: "Blue", 5: "Green", "Red": 0, "Green": 5, "Blue": 2}
// Color.Red → 0
// Color.Green → 5
// Color.Blue → 2
// Color[5] → Green
// Color.Blue > Color.Green → false
そして、これが lodash mixin です。文字列を使ってenumを作成します。このバージョンはもう少し複雑ですが、自動的に番号が付けられます。この例で使用されているすべてのlodashメソッドには通常のJavaScriptと同等のものがあるため、必要に応じて簡単に切り替えることができます。
function enum() {
var key, val = -1, list = {};
_.reduce(_.toArray(arguments), function(result, kvp) {
kvp = kvp.split("=");
key = _.trim(kvp[0]);
val = _.parseInt(kvp[1]) || ++val;
result[result[val] = key] = val;
return result;
}, list);
return Object.freeze(list);
}
// Add enum to lodash
_.mixin({ "enum": enum });
var Color = _.enum(
"Red",
"Green",
"Blue = 5",
"Yellow",
"Purple = 20",
"Gray"
);
// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue
これがTypeScriptがenum
をJavascriptに変換する方法です。
var makeEnum = function(obj) {
obj[ obj['Active'] = 1 ] = 'Active';
obj[ obj['Closed'] = 2 ] = 'Closed';
obj[ obj['Deleted'] = 3 ] = 'Deleted';
}
今:
makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}
最初はobj[1]
が'Active'
を返す理由を混同していましたが、それからその単純な - 代入演算子 が値を代入してそれを返すことに気付きました。
obj['foo'] = 1
// => 1
最も簡単な解決策:
var Status = Object.freeze({
"Connecting":0,
"Ready":1,
"Loading":2,
"Processing": 3
});
console.log(Status.Ready) // 1
console.log(Object.keys(Status)[Status.Ready]) // Ready
使い方は簡単だと思います。 https://stackoverflow.com/a/32245370/4365315
var A = {a:11, b:22},
enumA = new TypeHelper(A);
if(enumA.Value === A.b || enumA.Key === "a"){
...
}
var keys = enumA.getAsList();//[object, object]
//set
enumA.setType(22, false);//setType(val, isKey)
enumA.setType("a", true);
enumA.setTypeByIndex(1);
更新:
私のヘルパーコード(TypeHelper
)があります。
var Helper = {
isEmpty: function (obj) {
return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
},
isObject: function (obj) {
return (typeof obj === 'object');
},
sortObjectKeys: function (object) {
return Object.keys(object)
.sort(function (a, b) {
c = a - b;
return c
});
},
containsItem: function (arr, item) {
if (arr && Array.isArray(arr)) {
return arr.indexOf(item) > -1;
} else {
return arr === item;
}
},
pushArray: function (arr1, arr2) {
if (arr1 && arr2 && Array.isArray(arr1)) {
arr1.Push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
}
}
};
function TypeHelper() {
var _types = arguments[0],
_defTypeIndex = 0,
_currentType,
_value,
_allKeys = Helper.sortObjectKeys(_types);
if (arguments.length == 2) {
_defTypeIndex = arguments[1];
}
Object.defineProperties(this, {
Key: {
get: function () {
return _currentType;
},
set: function (val) {
_currentType.setType(val, true);
},
enumerable: true
},
Value: {
get: function () {
return _types[_currentType];
},
set: function (val) {
_value.setType(val, false);
},
enumerable: true
}
});
this.getAsList = function (keys) {
var list = [];
_allKeys.forEach(function (key, idx, array) {
if (key && _types[key]) {
if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
var json = {};
json.Key = key;
json.Value = _types[key];
Helper.pushArray(list, json);
}
}
});
return list;
};
this.setType = function (value, isKey) {
if (!Helper.isEmpty(value)) {
Object.keys(_types).forEach(function (key, idx, array) {
if (Helper.isObject(value)) {
if (value && value.Key == key) {
_currentType = key;
}
} else if (isKey) {
if (value && value.toString() == key.toString()) {
_currentType = key;
}
} else if (value && value.toString() == _types[key]) {
_currentType = key;
}
});
} else {
this.setDefaultType();
}
return isKey ? _types[_currentType] : _currentType;
};
this.setTypeByIndex = function (index) {
for (var i = 0; i < _allKeys.length; i++) {
if (index === i) {
_currentType = _allKeys[index];
break;
}
}
};
this.setDefaultType = function () {
this.setTypeByIndex(_defTypeIndex);
};
this.setDefaultType();
}
var TypeA = {
"-1": "Any",
"2": "2L",
"100": "100L",
"200": "200L",
"1000": "1000L"
};
var enumA = new TypeHelper(TypeA, 4);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
enumA.setType("200L", false);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
enumA.setDefaultType();
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
enumA.setTypeByIndex(1);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");
document.writeln("is equals = ", (enumA.Value == TypeA["2"]));
私はenumerationjsを書きました 問題に対処するための非常に小さなライブラリ whichtype safes、enum定数がプロトタイプからを継承できるようにし、enum定数とenum型が不変であることを保証します+多くの小さな機能多くのコードをリファクタリングし、enum定義内でいくつかのロジックを移動できます。以下に例を示します。
var CloseEventCodes = new Enumeration("closeEventCodes", {
CLOSE_NORMAL: { _id: 1000, info: "Connection closed normally" },
CLOSE_GOING_AWAY: { _id: 1001, info: "Connection closed going away" },
CLOSE_PROTOCOL_ERROR: { _id: 1002, info: "Connection closed due to protocol error" },
CLOSE_UNSUPPORTED: { _id: 1003, info: "Connection closed due to unsupported operation" },
CLOSE_NO_STATUS: { _id: 1005, info: "Connection closed with no status" },
CLOSE_ABNORMAL: { _id: 1006, info: "Connection closed abnormally" },
CLOSE_TOO_LARGE: { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
console.log(this.info);
}
});
CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true
Enumeration
は基本的にファクトリです。
完全に文書化されたガイドが利用可能です。 これが役立つことを願っています。
これを試すことができます:
var Enum = Object.freeze({
Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
});
alert(Enum.Role.Supervisor);
alert(Enum.Color.GREEN);
var currentColor=0;
if(currentColor == Enum.Color.RED) {
alert('Its Red');
}
es7 way、(イテレータ、フリーズ)、使用方法:
const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')
for (let name of ThreeWiseMen)
console.log(name)
// with a given key
let key = ThreeWiseMen.Melchior
console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)
for (let entry from key.enum)
console.log(entry)
// prevent alteration (throws TypeError in strict mode)
ThreeWiseMen.Me = 'Me too!'
ThreeWiseMen.Melchior.name = 'Foo'
コード:
class EnumKey {
constructor(props) { Object.freeze(Object.assign(this, props)) }
toString() { return this.name }
}
export class Enum {
constructor(...keys) {
for (let [index, key] of keys.entries()) {
Object.defineProperty(this, key, {
value: new EnumKey({ name:key, index, enum:this }),
enumerable: true,
})
}
Object.freeze(this)
}
*[Symbol.iterator]() {
for (let key of Object.keys(this))
yield this[key]
}
toString() { return [...this].join(', ') }
}
私はしばらく前に、JSのバージョンに応じて__defineGetter__
と__defineSetter__
またはdefineProperty
を組み合わせて使用しました。
これが私が作ったenum生成関数です: https://Gist.github.com/gfarrell/6716853
あなたはこのようにそれを使うでしょう:
var Colours = Enum('RED', 'GREEN', 'BLUE');
そしてそれは不変の文字列を作ります:int dictionary(enum)。
@Duncanが上で行ったことと同じように本当に好きですが、私はEnumでグローバルなObject関数空間を消すのが好きではないので、私は以下を書きました:
function mkenum_1()
{
var o = new Object();
var c = -1;
var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};
for (i in arguments) {
var e = arguments[i];
if ((!!e) & (e.constructor == Object))
for (j in e)
f(j, (c=e[j]));
else
f(e, ++c);
}
return Object.freeze ? Object.freeze(o) : o;
}
var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');
console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);
// Output is:
// MED := 1
// LRG := 100
@Stijinには、これらのオブジェクトのプロパティを含む、きちんとした解決策(彼のブログを参照)もあります。そのためのコードも書きました。
function mkenum_2(seed)
{
var p = {};
console.log("Seed := " + seed);
for (k in seed) {
var v = seed[k];
if (v instanceof Array)
p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
else
p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
}
seed.properties = p;
return Object.freeze ? Object.freeze(seed) : seed;
}
このバージョンは、わかりやすい名前変換と短いコードを許可する追加のプロパティリストを作成します。コードがあなたのためにそれをするように人がプロパティのデータ入力を複製する必要がないので私はこのバージョンが好きです。
var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });
これら2つを組み合わせて単一の処理単位mkenumにすることができます(列挙型の使用、値の割り当て、プロパティリストの作成と追加)。しかし、今日この記事ではすでにあまりにも多くの時間を費やしているので、この組み合わせは親愛なる読者のための課題として残します。
を使用することができます - Object.prototype.hasOwnProperty()
var findInEnum,
colorEnum = {
red : 0,
green : 1,
blue : 2
};
// later on
findInEnum = function (enumKey) {
if (colorEnum.hasOwnProperty(enumKey)) {
return enumKey+' Value: ' + colorEnum[enumKey]
}
}
alert(findInEnum("blue"))
my opinionのenumとは何ですか?それは常にアクセス可能な不変オブジェクトであり、項目同士を比較することはできますが、項目には共通のプロパティ/メソッドがありますが、オブジェクト自体または値は変更できず一度だけインスタンス化されます。
列挙型は、データ型、設定、そのようなことを行う/返信するためのアクションを比較するために使用されます。
そのため、これには同じインスタンスを持つオブジェクトが必要です。それがenum型かどうかを確認することができますif(something instanceof enum)
また、enum型を取得した場合、enum型に関係なくそれを使用できます。それはいつも同じように反応するはずです。
私の場合は、データ型の値を比較していますが、3Dゲームでブロックを対面方向に変更してから、特定のオブジェクト型レジストリに値を渡すまで、何でもかまいません。
それはjavascriptであり、固定のenum型を提供していないことを頭に入れておいてください、あなたは常にあなた自身の実装を作ることになります、そしてこのスレッドが示すように絶対に正しいものなしで実装の軍団があります。
これが私がEnumに使うものです。列挙型は不変であるため(または少なくともhehであるべきです)、オブジェクトをフリーズしますので、それらを簡単に操作することはできません。
EnumはEnumField.STRINGで使用することができ、それらはそれらの型で動作する独自のメソッドを持ちます。何かがオブジェクトに渡されたかどうかをテストするためにあなたはif(somevar instanceof EnumFieldSegment)
を使うことができます。
これは最も洗練された解決策ではないかもしれませんし、改善の余地がありますが、このタイプの不変の列挙型(あなたがそれを凍結解除しない限り)はまさに私が必要としたユースケースです。
プロトタイプを{}で上書きすることもできたことを私は認識していますが、私の頭の中ではこのフォーマットのほうがうまくいきます;-).
/**
* simple parameter object instantiator
* @param name
* @param value
* @returns
*/
function p(name,value) {
this.name = name;
this.value = value;
return Object.freeze(this);
}
/**
* EnumFieldSegmentBase
*/
function EnumFieldSegmentBase() {
this.fieldType = "STRING";
}
function dummyregex() {
}
dummyregex.prototype.test = function(str) {
if(this.fieldType === "STRING") {
maxlength = arguments[1];
return str.length <= maxlength;
}
return true;
};
dummyregexposer = new dummyregex();
EnumFieldSegmentBase.prototype.getInputRegex = function() {
switch(this.fieldType) {
case "STRING" : return dummyregexposer;
case "INT": return /^(\d+)?$/;
case "DECIMAL2": return /^\d+(\.\d{1,2}|\d+|\.)?$/;
case "DECIMAL8": return /^\d+(\.\d{1,8}|\d+|\.)?$/;
// boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its false, otherwise lets see what Boolean produces
case "BOOLEAN": return dummyregexposer;
}
};
EnumFieldSegmentBase.prototype.convertToType = function($input) {
var val = $input;
switch(this.fieldType) {
case "STRING" : val = $input;break;
case "INT": val==""? val=0 :val = parseInt($input);break;
case "DECIMAL2": if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break;
case "DECIMAL8": if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break;
// boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its false, otherwise lets see what Boolean produces
case "BOOLEAN": val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf())) ;break;
}
return val;
};
EnumFieldSegmentBase.prototype.convertToString = function($input) {
var val = $input;
switch(this.fieldType) {
case "STRING": val = $input;break;
case "INT": val = $input+"";break;
case "DECIMAL2": val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break;
case "DECIMAL8": val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break;
case "BOOLEAN": val = $input ? "true" : "false" ;break;
}
return val;
};
EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) {
var val = false;
switch(this.fieldType) {
case "STRING": val = ($val1===$val2);break;
case "INT": val = ($val1===$val2);break;
case "DECIMAL2": val = ($val1.comparedTo($val2)===0);break;
case "DECIMAL8": val = ($val1.comparedTo($val2)===0);break;
case "BOOLEAN": val = ($val1===$val2);break;
}
return val;
};
/**
* EnumFieldSegment is an individual segment in the
* EnumField
* @param $array An array consisting of object p
*/
function EnumFieldSegment() {
for(c=0;c<arguments.length;c++) {
if(arguments[c] instanceof p) {
this[arguments[c].name] = arguments[c].value;
}
}
return Object.freeze(this);
}
EnumFieldSegment.prototype = new EnumFieldSegmentBase();
EnumFieldSegment.prototype.constructor = EnumFieldSegment;
/**
* Simple enum to show what type of variable a Field type is.
* @param STRING
* @param INT
* @param DECIMAL2
* @param DECIMAL8
* @param BOOLEAN
*
*/
EnumField = Object.freeze({STRING: new EnumFieldSegment(new p("fieldType","STRING")),
INT: new EnumFieldSegment(new p("fieldType","INT")),
DECIMAL2: new EnumFieldSegment(new p("fieldType","DECIMAL2")),
DECIMAL8: new EnumFieldSegment(new p("fieldType","DECIMAL8")),
BOOLEAN: new EnumFieldSegment(new p("fieldType","BOOLEAN"))});
class Enum {
constructor (...vals) {
vals.forEach( (val, i) => {
Object.defineProperty(this, val.toUpperCase(), {
get () {
return i;
},
set (val) {
const enum_val = "CONSTANT";
// generate TypeError associated with attempting to change the value of a constant
enum_val = val;
}
});
});
}
}
使用例
const COLORS = new Enum("red", "blue", "green")
console.log(COLORS.RED) // 0
console.log(COLORS.GREEN) // 2
すべての回答を読んでも、冗長ではないDRY解決策は見つかりませんでした。私はこのワンライナーを使います:
const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});
人間が読める値でオブジェクトを生成します。
{
DRAW: 'DRAW',
SCALE: 'SCALE',
DRAG: 'DRAG'
}
エイリアンの解決策は、物事をできるだけ単純にすることです。
Enumキーワードが予約されているだけでJavaScriptに実装されていない場合は、次のように定義します。
const enumerate = spec => spec.split(/\s*,\s*/)
.reduce((e, n) => Object.assign(e,{[n]:n}), {})
今、あなたはそれを簡単に使うことができます
const kwords = enumerate("begin,end, procedure,if")
console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)
列挙値を明示的な変数にする理由はありません。スクリプトはとにかく形が悪く、コードの一部が文字列または有効なコードであっても違いはありません。本当に重要なのは、引用符を使用したり定義したりするたびに大量の引用符を扱う必要がないということです。
var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });
あなたはidを指定する必要はありません、あなたは単に列挙型を比較するために空のオブジェクトを使うことができます。
if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
EDIT: もしあなたがオブジェクトを(例えばJSONに)シリアライズするつもりなら、あなたは再びidを使います。
この答えは特定の状況に対する代替アプローチです。属性のサブ値に基づくビットマスク定数のセットが必要でした(属性値が配列または値のリストの場合)。それはいくつかの重複するenumの等価物を含みます。
ビットマスク値を格納して生成するためのクラスを作成しました。たとえば、緑がRGB値に含まれているかどうかをテストするために、このように疑似定数ビットマスク値を使用できます。
if (value & Ez.G) {...}
私のコードでは、このクラスのインスタンスを1つだけ作成しています。クラスのインスタンスを少なくとも1つインスタンス化せずにこれを実行するための明確な方法はないようです。これがクラス宣言とビットマスク値生成コードです。
class Ez {
constructor() {
let rgba = ["R", "G", "B", "A"];
let rgbm = rgba.slice();
rgbm.Push("M"); // for feColorMatrix values attribute
this.createValues(rgba);
this.createValues(["H", "S", "L"]);
this.createValues([rgba, rgbm]);
this.createValues([attX, attY, attW, attH]);
}
createValues(a) { // a for array
let i, j;
if (isA(a[0])) { // max 2 dimensions
let k = 1;
for (i of a[0]) {
for (j of a[1]) {
this[i + j] = k;
k *= 2;
}
}
}
else { // 1D array is simple loop
for (i = 0, j = 1; i < a.length; i++, j *= 2)
this[a[i]] = j;
}
}
2D配列はSVGのfeColorMatrix values属性用です。これは、RGBA x RGBAMの4×5行列です。ここで、Mは乗数です。結果のEzプロパティはEz.RR、Ez.RGなどです。
ES6以降、Enum自体は作成できませんが、少なくともVSCodeでは、コードの自動補完に適した、より洗練された構文を使用できます。
class MyEnum {
const A = '1'
const B = '2'
const C = '3'
}
プラスの面では、他の回答のようにあなたが望むものなら何でもconst
の中に置くことができます。また、Nodeでは、意味のある名前を維持しながら、モジュールの一部としてエクスポートすることができます。
import { MyEnum } from './my-enum'
console.log(MyEnum.B)
お役に立てれば。
単純な関数を使用してキーと値を逆にすることができます。これは数値整数文字列を数値に変換するので、配列でも機能します。コードは小さく、シンプルで、このような用途や他の用途に再利用できます。
var objInvert = function (obj) {
var invert = {}
for (var i in obj) {
if (i.match(/^\d+$/)) i = parseInt(i,10)
invert[obj[i]] = i
}
return invert
}
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL',
'BOSSA-NOVA','POP','INDIE']))
console.log(musicStyles)