JavaScriptで列挙型を定義するための推奨構文は何ですか?


2082

JavaScriptで列挙型を定義するための推奨構文は何ですか?何かのようなもの:

my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
   // whatever
}

または、より好ましいイディオムはありますか?


132
0列挙番号として使用しないでください。設定されていないものに使用しない限り。JSはfalse || undefined || null || 0 || "" || '' || NaN、を使用して比較すると、すべて同じ値として扱います==
matsko

153
@matskoは、==を使用することに対する単なる反対ではありませんか?
sdm350 2015

6
0 == nullfalseを返します
mcont

11
しかし、false == 0+null == 0(と数字への変換は、時にはあなたがそれを期待していない場合に発生)しながら、null == undefinedあまりにも、と+undefinedあるNaN(けれどもNaN != NaN)。
sanderd17

46
二重等式行列は、Microsoft Wordの自動フォーマットよりもわかりにくい
aaaaaa

回答:


896

1.8.5以降、オブジェクトをシールしてフリーズすることができるため、上記を次のように定義します。

const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})

または

const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)

そして出来上がり!JS列挙型。

ただし、これは望ましくない値を変数に割り当てることを妨げません。これは、列挙型の主な目的であることがよくあります。

let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors

列挙型などを使用してより強力な型安全性を確保する1つの方法は、TypeScriptFlowなどのツールを使用することです。

ソース

引用は必要ありませんが、一貫性を保つために引用しました。


6
Wikipedia(en.wikipedia.org/wiki/JavaScript#Versions)によると、Firefox 4、IE 9、Opera 11.60に適用でき、Chromeで動作することはわかっています。
Artur Czajka

77
これが2012年の正解です。もっと簡単ですvar DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });。IDを指定する必要はありません。空のオブジェクトを使用して列挙型を比較できます。if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
ガブリエルラマ

34
下位互換性のためにif (Object.freeze) { Object.freeze(DaysEnum); }
saluce

17
私が指摘しておきたいの({ monday: {}, は、stringifyを介してそのオブジェクトをJSONに変換すると、機能し[{"day": {}}]ないということです。
jcollum 2013

10
@Supuhstarこの質問に対する私の意見は今では異なります。freeze()を使用しないでください。完全に役に立たず、「愚かな」ことをするのに時間の無駄になります。列挙型を公開する場合は、単にこれを公開しますvar DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}。以前のコメントのようにオブジェクトを比較することは、数値を比較するよりもはるかに遅くなります。
ガブリエルラマ14

608

これは大した答えではありませんが、個人的にはそれでうまくいきます

とはいえ、値は何でもかまいません(0、1、2を使用したため)。現在の値を出力したい場合に備えて、意味のある文字列を使用します。


377
これは別の回答で述べられましたが、この回答は受け入れられた回答であるため、ここに投稿します。OPのソリューションは正しいです。ただし、と一緒に使用すると、さらに優れたものになりますObject.freeze()。これにより、他のコードが列挙型の値を変更できなくなります。例:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth 2014年

5
@TolgaEそのライブラリをありがとう!それは私がそれを最低限にまで煮詰めるだけでなく、いくつかの機能を追加することにも影響を与えました!私はあなたのものをフォークしてすべてここに入れました:github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar

3
@Supuhstarいいね!このライブラリに統合したい場合は、プルリクエストを送信してください。npmライブラリを更新できます
Tolga E '

2
誰かが興味を持っている場合は、Javaと同じようにタイプセーフな列挙型を実装しました。つまり、instanceofチェックを行うことができます。たとえばColorEnum.RED instanceof ColorEnum(戻り値true)。名前からインスタンスを解決することもできますColorEnum.fromName("RED") === ColorEnum.RED(を返しますtrue)。各インスタンスには.name().ordinal()メソッドもあり、列挙自体にはvalues()すべての定数の配列を返すメソッドがあります。
Vivin Paliath、2015

3
「意味のある文字列」の提案に同意するかどうかはわかりません。列挙型は文字列や数値と考えるべきではありません。それらは抽象的なデータ型です。ヘルパーメソッドがなければ、「現在の値を出力」することはできません。Javaと.NETでは、そのToString()メソッドです。私たちJS開発者はすでに「うまく機能している」ことに依存しすぎています!また、switch列挙型をすばやく処理できる必要があります。文字列の比較は数値の比較よりも遅いためswitch、整数の代わりに文字列を使用すると、パフォーマンスがわずかに低下します。
Rabadash8820 2017

501

更新

すべての賛成投票に感謝しますが、以下の私の答えがJavaScriptで列挙型を記述する最善の方法であるとは思いません。詳細については、ブログの投稿「JavaScriptの列挙型」を参照してください。


名前を警告することはすでに可能です:

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では動的言語であるため、後でenum値をセットに追加することも可能です。

// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};

enumのフィールド(この例では値、名前、コード)はIDチェックに必要ではなく、便宜上そこにのみ存在することに注意してください。また、サイズプロパティ自体の名前をハードコーディングする必要はありませんが、動的に設定することもできます。したがって、新しい列挙値の名前しかわからない場合でも、問題なく追加できます。

// 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


では、名前しかわからない場合、どのようにして単純にSIZEを作成するのでしょうか。
ヨハニマ

2
@Johanisma:enumの全体的な考えは、すべての値を事前に知っているということなので、そのユースケースはenumにとって実際には意味がありません。ただし、JavaScriptで後で値を追加することを妨げるものは何もありません。その例を回答に追加します。
Stijn de Witt

2
プロパティアプローチを使用した投稿へのリンクの+1。OPのように基本的な宣言が単純であり、必要に応じてプロパティ機能が追加されるという点でエレガントです。
goodeye 2014

@Stijin、更新されたソリューションが本当に気に入りました。ブログのコメントと以下のコメントにコードを投稿しました。基本的に、関数を使用して、既存のハッシュリストからプロパティビルドを実行し、オプションでそれをフリーズします(リストのmkenum_2)。乾杯。
アンドリューフィリップス

それを実装するライブラリもあり、比較と逆検索に関する素晴らしい機能も含まれています。github.com
Roman M

83

結論:できません。

偽造することはできますが、タイプセーフは得られません。通常、これは整数値にマップされた文字列値の単純な辞書を作成することによって行われます。例えば:

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
列挙型を作成するための非常に優れたライブラリを次に示します。

http://www.2ality.com/2011/10/enums.html

列挙型のすべての有効な使用法に適合しない可能性がありますが、非常に長い道のりです。


103
JavaScriptにはタイプセーフがありますか?
スコットエバーデン2009

3
したがって、値をオブジェクトのプロパティにマップしないでください。getterを使用して列挙型(たとえば、「プライベート」オブジェクトのプロパティとして格納されます)にアクセスします。素朴な実装は次のようになりますvar daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax 2009

2
@Scott Evernden:要点。@kangax:ポイントは、それがまだハックであるということです。列挙型は、Javascript、ピリオド、ストーリーの終わりには存在しません。Tim Sylvesterによって提案されたパターンでさえ、まだ理想的なハックではありません。
Randolpho

2
コードにリテラルを散りばめることはあまり維持できないため、定数を作成することは理にかなっています。もちろん、JavaScriptにも定数はありません。基本的に、これはクリーンなコードを書くための方法にすぎません。強制することはできませんが、Javascriptではそれほどではありません。定数、関数、またはほとんどすべてを再定義できます。EG:document.getElementById = function(){alert( "あなたはねじ込まれています。Javascriptはタイプセーフではありません。");};
Stijn de Witt

3
@Randolpho:Artur CzajkaのObject.freezeはどうですか?月曜日を木曜日に設定できないようにするにはどうすればよいでしょうか。
マイケル-クレイシャーキーは

56

ここに私たち全員が欲しいものがあります:

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 */
};


編集-小さな改善-varargs:(残念ながら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');

この答えのシンプルさが大好きです!
Marquizzo

@Marquizzo(およびOP)この回答に基づいて改良版を作成しました:stackoverflow.com/a/60309416/1599699
Andrew

53

最近のほとんどのブラウザーには、列挙型の作成に使用できるシンボルプリミティブデータ型があります。各シンボル値はJavaScriptによって一意であることが保証されているため、列挙型のタイプセーフが保証されますSymbol() != Symbol()。例えば:

const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});

デバッグを簡略化するために、列挙値に説明を追加できます。

const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});

プランカーのデモ

GitHubのあなたは、列挙型を初期化するために必要なコードを簡素化するラッパーを見つけることができます。

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

これは理論的には正しい答えです。実際には、2015年のブラウザーサポートは十分ではありません。はるかに生産準備ができていません。
vbraun

1
ブラウザのサポートはまだありませんが、これSymbolは意図されているものに近いので、これが最良の答えです。
rvighne

2
Meh ...列挙値はしばしばシリアライズ可能である必要があり、シンボルはシリアライズおよびデシリアライズするのにそれほど便利ではありません。
アンディ

3
それは私だけですかObject.freeze、それとも「あなた自身の責任でのモンキーパッチ」がJSの社会契約であるという事実を受け入れていない人々のためのものですか?
アンディ

@Andyはい、シリアル化は厄介です。私は明示的にやってしまったtoJSONこのアプローチを使用することを含むクラス上:stackoverflow.com/questions/58499828/...
チロSantilli冠状病毒审查六四事件法轮功

30

𝗣𝗹𝗮𝗶𝗻𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝗡𝗮𝗺𝗲𝘀

問題を直視しましょう:ファイルサイズ。ここに記載されている他のすべての回答は、コードを極端に膨らませています。可能な限り最高のパフォーマンス、コードの可読性、大規模なプロジェクト管理、多くのコードエディターでのシンタックスヒント、および縮小によるコードサイズの縮小のために、これが列挙を行う正しい方法であることを示します。アンダースコア表記変数です。


wvwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

アンダースコア表記変数

上記のチャートと以下の例に示されているように、開始するための5つの簡単な手順を次に示します。

  1. 列挙グループの名前を決定します。列挙の目的または少なくとも列挙のエントリを説明できる名詞を考えてください。たとえば、ユーザーが選択可能な色を表す列挙体のグループは、COLORSよりもCOLORCHOICESという名前にすることができます。
  2. グループ内の列挙が相互に排他的であるか独立しているかを決定します。相互に排他的な場合は、列挙された各変数名をで始めますENUM_。独立または並べて使用する場合は、を使用しますINDEX_
  3. エントリごとに、名前がENUM_またはINDEX_で始まる新しいローカル変数を作成し、次にグループの名前、アンダースコア、プロパティの一意のわかりやすい名前を作成します
  4. 追加ENUMLENGTH_ENUMLEN_INDEXLENGTH_、またはINDEXLEN_(かどうLEN_LENGTH_個人的な好みですが)一番最後に変数を列挙しました。列挙型に追加のエントリを追加してこの値をインクリメントしてもコードが破損しないように、コードでは可能な限りこの変数を使用する必要があります。
  5. それぞれの連続した列挙型変数に値1を与えるより最後よりも、0から始まるそこ発言があることこのページでコメントしている0ので、列挙値として使用すべきではないが0 == null0 == false0 == ""、および他のJSの狂気。この問題を回避し、同時にパフォーマンスを向上===させるために、常に使用し、(ex )==を除いてコードに表示しないようにしてください。私が長年使用してきた中で、列挙値として0を使用することに問題があったことは一度もありません。それでもきしむ場合は、多くの場合、パフォーマンスを低下させることなく、列挙で(列挙ではなく)開始値として使用できます。typeoftypeof X == "string"===1ENUM_INDEX_
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

これがINDEX_、いつ使用するか、いつ使用するかを覚えている方法ですENUM_

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

ただし、ENUM_特定の状況では、各アイテムの出現回数をカウントするときなどのインデックスとして適切です。

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

上記のコードでは、新しい種類のペットを追加するのが非常に簡単であることを確認してください。後に新しいエントリを追加し、それに応じてENUM_PET_RAT更新する必要がありますENUMLEN_PET。他の列挙システムに新しいエントリを追加することは、より困難でバグが多いかもしれません。


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvvwvvw wvvwwwwwwvvvw wvwwwww

𝗘𝘅𝘁𝗲𝗻𝗱𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀𝗪𝗶𝘁𝗵𝗔𝗱𝗱𝗶𝘁𝗶𝗼𝗻

さらに、この列挙の構文により、以下に示すように、明確で簡潔なクラス拡張が可能になります。クラスを拡張するには、増分する番号をLEN_親クラスのエントリに追加します。次に、サブクラスを独自のLEN_エントリで完成させ、将来サブクラスをさらに拡張できるようにします。

追加延長図

(function(window){
    "use strict";
    var parseInt = window.parseInt;

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

    // the red component of green
    console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]);
    // the alpha of transparent purple
    console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); 
    // the enumerated array for turquoise
    console.log(parseHexColor("#40E0D0"));
})(self);

(長さ:2,450バイト)

これは他のソリューションよりも実用的でないと言う人もいます。それは、大量のスペースを浪費し、書くのに長い時間がかかり、砂糖の構文で覆われていません。それらの人々が彼らのコードを縮小しないならば、それらは正しいでしょう。しかし、合理的な人が最終製品に非縮小コードを残すことはありません。この縮小のために、Closure Compilerは私がまだ見つけていない最高のものです。オンラインアクセスはここにあります。クロージャコンパイラは、この列挙データをすべて取得してインライン化できるため、Javascriptを非常に小さくして、非常に高速に実行できます。したがって、Closure Compilerで縮小します。観察する。


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvvwvvw wvvwwwwwwvvvw wvwwwww

𝗪𝗶𝘁𝗵𝗠𝗶𝗻𝗶𝗳𝘆 𝗖𝗹𝗼𝘀𝘂𝗿𝗲𝗖𝗼𝗺𝗽𝗶𝗹𝗲𝗿

クロージャーコンパイラーは、他のJavascriptミニファイアの能力をはるかに超える推論を介して、かなり驚くべき最適化を実行できます。Closure Compilerは、固定値に設定されたプリミティブ変数をインライン化できます。Closure Compilerは、これらのインライン化された値に基づいて推論を行い、ifステートメントとループで未使用のブロックを排除することもできます。

Closure Compilerによるコードの絞り込み

'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);

(長さ:605バイト)

Closure Compilerは、よりスマートにコーディングしてコードを適切に整理することで報酬を提供します。なぜなら、多くの縮小版では整理されたコードをより大きな縮小ファイルサイズで罰しますが、Closure Compilerはすべてのクリーンさと健全性をふるいにかけて、トリックを使用すればさらに小さなファイルサイズを出力できます。変数名の列挙のように。それが、この1つの心の中でのコーディングの聖杯です。小さなサイズでコードを支援し、より良いプログラミングの習慣を訓練することで心を支援するツールです。


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvvwvvw wvvwwwwwwvvvw wvwwwww

𝗦𝗺𝗮𝗹𝗹𝗲𝗿𝗖𝗼𝗱𝗲𝗦𝗶𝘇𝗲

ここで、これらの列挙を使用しない場合の同等のファイルの大きさを見てみましょう。

列挙型を使用しないソース(長さ:1,973バイト(列挙型コードよりも477バイト短い!))
列挙型を使用しないで縮小(長さ:843バイト(列挙型コードよりも 238バイト長い))

コードサイズのチャート



ご覧のように、列挙を使用しないと、ソースコードは短くなりますが、縮小されたコードが大きくなります。あなたのことは知りません。しかし、ソースコードを最終製品に組み込まないことは確かです。したがって、この形式の列挙は、縮小されたファイルサイズが小さくなるという点ではるかに優れています。


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvvwvvw wvvwwwwwwvvvw wvwwwww

𝗖𝗼𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝘃𝗲🤝𝗕𝘂𝗴𝗙𝗶𝘅𝗶𝗻𝗴

この形式の列挙のもう1つの利点は、コードサイズを縮小することなく、大規模プロジェクトを簡単に管理できることです。他の多くの人がいる大規模なプロジェクトで作業する場合、コードの作成者を明示して変数名にラベルを付けてラベルを付けると、共同でバグを修正するためにコードの元の作成者をすばやく特定できるため便利です。

// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED   = 0,
      ENUM_JG_COLORENUM_GREEN = 1,
      ENUM_JG_COLORENUM_BLUE  = 2,
      ENUMLEN_JG_COLORENUM    = 3;

// later on

if(currentColor === ENUM_JG_COLORENUM_RED) {
   // whatever
}

// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED   = 0,
      ENUM_PL_ARRAYTYPE_ISSORTED   = 1,
      ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob Knight
      ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
      ENUMLEN_PL_COLORENUM         = 4;

// later on

if(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

𝗦𝘂𝗽𝗲𝗿𝗶𝗼𝗿𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

さらに、この列挙の形式は、縮小後もはるかに高速です。通常の名前付きプロパティでは、ブラウザはハッシュマップを使用して、プロパティがオブジェクトのどこにあるかを調べる必要があります。JITコンパイラはオブジェクト上のこの場所をインテリジェントにキャッシュしますが、オブジェクトからより低いプロパティを削除するなどの特別な場合のために、依然として多大なオーバーヘッドがあります。

ただし、連続した非スパース整数インデックスのPACKED_ELEMENTS配列では、内部配列の値のインデックスがすでに指定されているため、ブラウザーはそのオーバーヘッドの多くをスキップできます。はい、ECMAScript標準に従って、すべてのプロパティは文字列として扱われることになっています。それでも、ECMAScript標準のこの側面は、すべてのブラウザーが配列内の数値インデックスに対して特別な最適化を行っているため、パフォーマンスに関して非常に誤解を招きます。

/// Hashmaps are slow, even with JIT juice
var ref = {};
ref.count = 10;
ref.value = "foobar";

上記のコードと以下のコードを比較してください。

/// Arrays, however, are always lightning fast
const INDEX_REFERENCE_COUNT = 0;
const INDEX_REFERENCE_VALUE = 1;
const INDEXLENGTH_REFERENCE = 2;

var ref = [];
ref[INDEX_REFERENCE_COUNT] = 10;
ref[INDEX_REFERENCE_VALUE] = "foobar";

列挙型のコードは、通常のオブジェクトのコードよりもはるかに長いように見えるかもしれませんが、見た目が間違っている可能性があります。Epic Closure Compilerを使用する場合、ソースコードのサイズは出力サイズに比例しないことに注意してください。観察する。

/// Hashmaps are slow, even with JIT juice
var a={count:10,value:"foobar"};

列挙のない縮小コードは上にあり、列挙のある縮小コードは下にあります。

/// Arrays, however, are always lightning fast
var a=[10,"foobar"];

上記の例は、優れたパフォーマンスに加えて、列挙されたコードにより縮小されたファイルサイズが小さくなることを示しています。


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvvwvvw wvvwwwwwwvvvw wvwwwww

𝗘𝗮𝘀𝘆𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

さらに、一番上のこの個人のチェリーは、 JavaScriptモードでCodeMirrorテキストエディターと共にこの形式の列挙を使用しています。CodeMirrorのJavaScript構文強調表示モードは、現在のスコープ内のローカル変数を強調表示します。これにより、変数名を正しく入力したことがすぐにわかります。変数名が以前にvarキーワードで宣言されていた場合、変数名は特別な色(デフォルトではシアン)になるためです。CodeMirrorを使用しない場合でも、少なくともブラウザーは有用な[variable name] is not defined列挙名を誤って入力したコードを実行すると例外が発生します。また、JSLintやClosure CompilerなどのJavaScriptツールは、列挙型の変数名を誤って入力したときに通知することについて非常に騒々しいです。CodeMirror、ブラウザ、およびさまざまなJavaScriptツールを組み合わせることで、この形式の列挙のデバッグを非常に単純かつ非常に簡単に行うことができます。

CodeMirror強調表示のデモ

const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;
var currentColor = ENUM_COLORENUM_GREEN;

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

if(currentColor === ENUM_COLORENUM_DNE) {
   // whatever
}

上記のスニペットでENUM_COLORENUM_DNEは、存在しないため、エラーが発生しました。


wvwwvw wvwvwvw wvwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvvwvvw wvvwwwwwwvvvw wvwwwww

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻☑

この列挙の方法論は、コードサイズの縮小だけでなく、パフォーマンス、デバッグ、およびコラボレーションにも最適な方法であると言っても差し支えないと思います。

役立つ質問を読んだ後、質問ボックスの左上の上矢印をクリックして、執筆に時間を費やしてくれた作者に感謝します。各回答ボックスにも、これらの上向き矢印の1つがあります。


ええ 私は読みやすさと使いやすさ、コードサイズの理解を強く望んでいます。
Andrew

1
@Andrew私の答えでは、両方を持つことができます。使用するのが最も簡単で私の答えの結果は/コードを管理し、最小縮小さコードsize.🙂中
ジャックGiffin

1
@Andrew私はあなたのYet Another Enum(YEA!)を私の回答のカラーパーサーの例に適用しようとしました。ただし、対処する必要のある問題がいくつか見つかりました。YEAには列挙型をサブクラスで拡張する方法がないため、親プロジェクトと子クラスを別々に作成する必要があり、大規模なプロジェクトでは管理が非常に困難になる可能性があります。YEAは、エントリが(元の存在を保証しませんcolors.REED利回りをundefined)ので、タイプミスは、とらえどころのない難問を作成します。YEAは、インデックスとIDとしての列挙の使用を区別しないため、すべてが同じに見えるコードが混乱します。…
ジャックギフィン

1
@Andrew…YEAはClosure Compilerの縮小能力を妨げます。YEAのソースコード(3549バイト)とYEAの縮小コード(1344バイト)を私のソリューションの縮小コード(604バイト)と比較します。最後に、YEAは文字列名を列挙されたIDから分離するため、「名前によるマッピング」を含みます。鉱山ではIDのみが考慮されるため、「名前によるマッピング」は不要であり、設計がシンプルになり、パフォーマンスが向上します。ソリューションを共有していただきありがとうございますが、実際に使用するには多くの修正が必要です。
ジャックギフィン

1
私は👍鉱山に午前とあなたはあなたの意見に権利がある@Andrew
ジャックGiffin

23

私は自分の列挙型を愛しているので、これをいじっています。=)

使用してObject.defineProperty私はやや実行可能な解決策を思いついたと思います。

ここにjsfiddleがあります:http ://jsfiddle.net/ZV4A6/

このメソッドを使用すると、(理論的には)そのオブジェクトの他の属性に影響を与えることなく、任意のオブジェクトの列挙値を呼び出して定義できるはずです。

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、これは必要があり、それはタイプセーフにします。

したがって、カスタムオブジェクトを作成し、それを呼び出すことができるはずEnum()です。割り当てられる値は0から始まり、項目ごとに増加します。

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

3
あなたが追加した場合return this;列挙型の最後に、あなたが行うことができます:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

それは私の通常のやり方ではないので、私はそれを考慮しませんでした。しかし、あなたは絶対に正しいです!それを編集します
ダンカン

(グローバル関数ENUMを使って)オブジェクト空間をいじるのはあまり好きではありませんが、私はこれが本当に好きです。これをmkenum関数に変換し、オプションの数値割り当てを追加しました=> var mixedUp = mkenum( 'BLACK'、{RED:0x0F00、BLUE:0X0F、GREEN:0x0F0、WHITE:0x0FFF、ONE:1}、TWO、THREE、FOUR) ; //以下の回答としてコードを追加します。ありがとう。
Andrew Philips

正直なところ、これはもう使っていません。私はGoogleのClosure Compilerを使用してきましたが、詳細設定を使用した場合、これはあまりうまく機能しません(または複雑になるだけです)。したがって、標準のオブジェクト表記法に戻りました。
ダンカン

1
falseデフォルトでwritableenumerableconfigurable。デフォルトをかみ砕く必要はありません。
2015年

23

JavaScript プロキシを使用する

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',
});

完全な説明:

従来の言語から得られるEnumの非常に有益な機能の1つは、存在しない列挙子にアクセスしようとすると、エラーが発生する(コンパイル時エラーがスローされる)ことです。

モックされた列挙型構造をフリーズして、誤って/悪意を持って追加の値が追加されるのを防ぐ以外に、他のどの回答もEnumの固有の機能に対応していません。

ご存知のように、JavaScriptで存在しないメンバーにアクセスすると、 undefinedしないと、、コードがありません。列挙子は事前定義された定数(曜日)であるため、列挙子を未定義にする必要はありません。

誤解しないでください。undefined未定義のプロパティにアクセスしたときに戻るというJavaScriptの動作は、実際には言語の非常に強力な機能ですが、従来のEnum構造を模倣しようとしたときに必要な機能ではありません。

これは、プロキシオブジェクトが輝く場所です。プロキシは、ES6(ES2015)の導入により言語で標準化されました。MDNからの説明は次のとおりです。

Proxyオブジェクトは、基本的な操作(プロパティの検索、割り当て、列挙、関数の呼び出しなど)のカスタム動作を定義するために使用されます。

Webサーバープロキシと同様に、JavaScriptプロキシはオブジェクトの操作をインターセプトでき(「トラップ」を使用し、必要に応じてフックと呼びます)、完了する前にさまざまなチェック、アクション、操作を実行できます(または場合によっては、操作を完全に停止します。これは、存在しない列挙子を参照しようとした場合にまさに実行したいことです)。

以下は、Proxyオブジェクトを使用してEnumを模倣した不自然な例です。この例の列挙子は、標準の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:一体何がプロキシですか?

私がどこでもプロキシという言葉を初めて見始めたとき、それは確かに長い間私には意味がありませんでした。それが今のあなたなら、プロキシを一般化する簡単な方法は、プロキシをソフトウェア、機関、または2つのサーバー、企業、または人々の間の仲介者または仲介者として機能する人々と考えることです。


myEnum.valueOf( "someStringValue")のようなことをするには?期待:入力文字列が列挙子の要素の値を持つ場合、アイテムを返す必要があります。その文字列値を持つアイテムがない場合は、例外をスローします。
sscarduzio

@sscarduzio valueOfでは、Enumクラスのインスタンスメソッドとして指定することで、デフォルトのメソッドをオーバーライドできます。しかし、なぜドット表記でアクセスするのではなく、このようにアクセスしたいのですか?
Govind Rai

私の列挙型はconst logLevelEnum = new Enum({INFO: "info"、DEBUG: "debug"})で、入力から任意の文字列 "info"または "debug"を解析します。そのため、currentLogLevel = logLevelEnum.parseOrThrow(settings.get( "log_level"))のようなものが必要です
sscarduzio

1
どうしてあなただけができないのlogLevelEnum[settings.get("log_level")]?追加parseOrThrowすることは、プロキシトラップがすでに実行していることの繰り返しになります。
Govind Rai

17

これは私が知っている古いものですが、TypeScriptインターフェイスを介して実装された方法は次のとおりです。

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Foo"] = 0] = "Foo";
    MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
    MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));

これにより、宣言の順序に関係なく、MyEnum.Bar1 MyEnum[1]を返すものと「Bar」を返すものの両方を検索できます。


1
プラスMyEnum ["Bar"]は1を返します... <3 TypeScriptこれまで...
DavidKarlašJan

3
そしてもちろん、もしあなたが実際にTypescriptを使用している場合:enum MyEnum { Foo, Bar, Foobar }
議会

16

ではES7、あなたは静的属性に頼るエレガントENUMを行うことができます。

class ColorEnum  {
    static RED = 0 ;
    static GREEN = 1;
    static BLUE = 2;
}

その後

if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}

(リテラルオブジェクトの代わりにクラスを使用することの)利点は、親クラスを持つことであり、EnumすべてのEnumがそのクラスを拡張します。

 class ColorEnum  extends Enum {/*....*/}

4
親クラスがあることの利点を説明してください。何か足りない気がします!
Jon G

7
それをしないでください。new ColorEnum()まったく意味がありません。
Bergi、2017年

3
列挙型の拡張は本当におかしく聞こえる
Codii 2017

言語がネイティブでサポートしていない場合は、この規則を守り、このように使用するのが理にかなっています。同意する!
xpto 2017年

(?):純粋な静的の利点は、それがシングルトンとしてどこでも利用可能であるということです、あなたはしていない私がある、OPができているものだと思う必要がある - OPのは、あなたが行うことを示唆していないクラスをインスタンス化するために!私は彼が何を言っていることはスーパーがあることだと思うEnum標準持つ静的に、その上に列挙法を、のようなgetValues()getNames()iterate()、などそのような場合、あなたはそれぞれの新しい種類のためにそれらを再実装する必要はありませんenum
エンジニア

15

これは私が使用するソリューションです。

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'
    }
});

そしてこれがあなたの列挙型にアクセスする方法です:

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
});

通常、最後の2つのメソッドを使用して、メッセージオブジェクトから列挙型をマッピングします。

このアプローチのいくつかの利点:

  • 列挙型の宣言が簡単
  • enumへのアクセスが簡単
  • 列挙型は複雑な型にすることができます
  • getByValueを頻繁に使用している場合、Enumクラスにはいくつかの連想キャッシュがあります

いくつかの欠点:

  • 列挙型への参照を保持しているため、いくつかの厄介なメモリ管理がそこで行われています
  • まだタイプセーフはありません

14

オブジェクトリテラルを作成します。

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};

12
constオブジェクトのプロパティが不変になるModesわけではありません。それは、変数を他の何かに再割り当てできないことを意味します。より完全にするために、Object.freeze()と一緒に使用しconstます。
rvighne

使用しないでくださいObject.freeze。Closure Compilerがオブジェクトをインライン化しないようにします。
ジャックギフィン2018年

11

あなたが使用している場合バックボーンを、あなたは自由に使用するための(ID、名前、カスタムメンバーで見つける)本格的な列挙機能を得ることができますBackbone.Collection

// 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()

8

あなたの答えはあまりにも複雑です

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

1
@JackGiffin私はあなたの答えがより高性能であり、私がより多くのメモリを消費する可能性があることに同意しますが、誰もがC ++が実装した方法の列挙型を望んでいると想定すべきではありません。他の回答と、あなたよりもこの回答を好む可能性のある開発者を尊重してください。
Xeltor 2018

7

アンドレ「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

6

私はJavaの列挙型をモデルにしたこのアプローチを思いつきました。これらはタイプセーフなので、実行できますinstanceofチェック。

次のように列挙型を定義できます。

var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);

DaysDays列挙型を参照するようになりました:

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
    }

})();

見栄えがいいfreezeです。下位互換性を保つために、メソッドの存在を確認する必要がありますか?例:if (Object.freeze) { Object.freeze(values); }
FBB

いい視点ね!しましょう!
Vivin Paliath、2015

6

IE8は、freeze()メソッドをサポートしていません。
ソース:http : //kangax.github.io/compat-table/es5/、「古いブラウザーを表示しますか?」をクリックします。上で、IE8を確認し、行と列の交差をフリーズします。

現在のゲームプロジェクトでは、まだ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'
};

最後の1つは、文字列に対して最も効率的であるように見えます。サーバーとクライアントがこのデータを交換している場合、総帯域幅が減少します。
もちろん、データに矛盾がないことを確認する必要があります(RE、EXなどは一意である必要があり、1、2なども一意である必要があります)。下位互換性のためにこれらを永久に維持する必要があることに注意してください。

割り当て:

var wildType = CONST_WILD_TYPES.REGULAR;

比較:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}

5
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

この方法で、異なる列挙値に重複した番号を割り当てないようにする必要はありません。新しいオブジェクトがインスタンス化され、すべての列挙値に割り当てられます。


この答えは過小評価されています。それはそのシンプルさのために私のお気に入りのアイデアの1つです。今のところデバッグが簡単なので、実際には文字列にこだわると思います。
ドミノ

うーん、このコードが2度呼び出されないようにしてください...
Andrew

4

TypeScript列挙型を実装する方法はいくつかあります

最も簡単な方法は、オブジェクトを反復処理して、反転したKey-Valueペアをオブジェクトに追加することです。唯一の欠点は、各メンバーの値を手動で設定する必要があることです。

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ミックスインです。このバージョンは少し複雑ですが、自動的に番号が付けられます。この例で使用されているすべての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

非常に賢い、ありがとう
イラン

4

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_enumconstjsステートメントを変更するには

var genEnum = require('gen_enum');

var genEnum = require('constjs').enum;

4

最も簡単なソリューション:

作成する

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

4

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選択オプションを簡単に取得することもできます。

var html = enum1.GetSelectOptionsHTML();

保持します:

"<option value='0'>item1</option>..."

4

にもかかわらず静的メソッドのみ(とない静的プロパティ)が(参照ES2015でサポートされているここだけでなく、§15.2.2.2)、不思議あなたがバベルと下に使用することができますes2015プリセット:

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」列挙型」ではなく、他の列挙型(一般的なオブジェクトまたはシンボルを使用した場合は区別できません)。

更新

上記のコードには、タイプの追加オブジェクトを作成できるという欠点がありますCellState(ただし、CellStateフリーズされているため、静的フィールドにオブジェクトを割り当てることはできません)。それでも、以下のより洗練されたコードには次の利点があります。

  1. タイプのオブジェクトはもうありません CellState作成できません
  2. 2つの列挙型インスタンスに同じコードが割り当てられないことが保証されます
  3. 文字列表現から列挙型を取得するユーティリティメソッド
  4. 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;

良い例:-)
Ashraf.Shk786 2018年

4

es7方法、(イテレーター、フリーズ)、使用法:

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(', ') }

}

4

これは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

4

あなたはこのようなことをすることができます

    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


3

すばやく簡単な方法は次のとおりです。

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

console.log(Colors.WHITE)  //this prints out "0"

6
この機能は不要であり、OPが投稿したものとまったく同じ結果が得られます。
Sildoreth 2014年

3

これを書いている時点で、2014年10月 -ここに現代的な解決策があります。Node Moduleとしてソリューションを作成しており、MochaとChai、およびunderscoreJSを使用したテストが含まれています。これらは簡単に無視でき、必要に応じてEnumコードを使用できます。

過度に複雑なライブラリなどの投稿が多数見られました。JavaScriptで列挙型のサポートを取得するためのソリューションは非常に単純なので、実際には必要ありません。これがコードです:

ファイル: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をエクスポートできます。

繰り返しになりますが、もしあなたが単にカジュアルなユーザーであるか、ブラウザなどの場合は、コードのファクトリー部分を取り、コードで使用したくない場合はアンダースコアライブラリも削除する可能性があります。


5
「工場、アンダースコア、または空想的なものではなく、列挙型を必要とするカジュアルなユーザーとしてこれを行う方法は次のとおりです」とだけ答えを投稿できますか?
GreenAsJade、2015年

5
これは開発者の観点からは非常にすばらしいものですが、あまりクリーンでなく読みやすいものでもありません。OPからのEnumソリューションは、あらゆる方法でより簡単で読みやすいため、使用するのが適切です。それでも、これを思いついたのはかなりかっこいいです。
デビッド

3

使いやすいと思います。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)があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.