javascript - 配列 - typescript enum




JavaScriptでenumを定義するための推奨構文は何ですか? (20)

Javascript Proxies使用する

TLDR:このクラスをユーティリティメソッドに追加し、コード全体で使用すると、従来のプログラミング言語からのEnumの動作を模倣し、存在しない列挙子にアクセスしようとすると実際にエラーをスローします。

class Enum {
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (target[name]) {
          return target[name];
        }
        throw new Error(`No such enumerator: ${name}`);
      },
    };

    return new Proxy(Object.freeze(enumObj), handler);
  }
}

次に、クラスをインスタンス化してenumを作成します。

const roles = new Enum({
  ADMIN: 'Admin',
  USER: 'User',
});

説明:

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

付加価値が誤って/悪意を持って追加されるのを防ぐために模擬したenum構造をフリーズする以外に、Enumの本質的な特徴に対処する答えはありません。

ご存知のように、JavaScriptで存在しないメンバーにアクセスすると、 undefinedが返され、コードが爆発することはありません。 列挙子はあらかじめ定義された定数(つまり、曜日)であるため、列挙子を定義しない場合はありません。

私が間違ってはいけません。未定義のプロパティにアクセスするときにJavaScriptが返すundefinedの動作は、実際には言語の非常に強力な機能ですが、従来の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(enumObj) {
  // 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. This can be done by defining the "get" trap.
  
  const handler = {
    get: function(target, name) {
      if (target[name]) {
        return target[name]
      }
      throw new Error(`No such enumerator: ${name}`)
    }
  }
  
  
  // Freeze the target object to prevent modifications
  return new Proxy(Object.freeze(enumObj), handler)
}


// Now that we have a generic way of creating Enums, lets create our first Enum!
const httpMethods = createEnum({
  DELETE: "DELETE",
  GET: "GET",
  OPTIONS: "OPTIONS",
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT"
})

// Sanity checks
console.log(httpMethods.DELETE) 
// logs "DELETE"

httpMethods.delete = "delete" 
// no effect due to Object.freeze, fails silently (no error thrown)

try {
  console.log(httpMethods.delete) 
} catch (e) {
  console.log("Error: ", e.message)
}
// throws an error "Uncaught Error: No such enumerator: delete"

ASIDE:プロキシは何ですか?

どこにでもプロキシという言葉が見え始めたのは覚えています。それは間違いなく長い間私にとって意味をなさないものでした。 それが今の場合、プロキシを一般化する簡単な方法は、それらをソフトウェア、機関、または2つのサーバー、企業、または人の間の仲介者または仲介者として行動する人として考えることです。

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

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 enums。

しかし、これは、変数に望ましくない値を割り当てることを妨げるものではありません。これはしばしば列挙型の主な目標です。

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

型の安全性(列挙型など)を強化する方法の1つは、 TypeScriptFlowなどのツールを使用することです。

Source

引用は必要ありませんが、私はそれらを一貫性のために保っていました。


IE8はfreeze()メソッドをサポートしていません。
出典: http://kangax.github.io/compat-table/es5/ : 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'
};

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

割り当て:

var wildType = CONST_WILD_TYPES.REGULAR;

比較:

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

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

次のようにinit'dできます:

var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

値を取得するには(C#のEnumsのように):

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>..."

ES7では、静的属性に依存するエレガントなENUMを実行できます。

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

次に

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

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

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

更新 :upvotesすべての人のおかげで、私は以下の私の答えは、もうJavascriptで列挙型を書くための最良の方法だとは思わない。 詳細は私のブログ記事を参照してください: Enums in 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では、動的言語なので、後で列挙型の値をセットに追加することも可能です。

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

列挙型のフィールド(この例では値、名前、コード)はIDチェックには必要ないことを覚えておいてください。 また、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のシンプルで強力なネームスペースと依存関係管理のための私のソリューションを見てみたいかもしれません: Packages JS


あなたの答えははるかに複雑です

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

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

function Enum(){
  this.add.apply(this,arguments);
}

Enum.prototype.add = function(){
  for (var i in arguments) {
    this[arguments[i]] = new String(arguments[i]);
  }
};
Enum.prototype.toList = function(){
  return Object.keys(this)
};

var STATUS = new Enum("CLOSED","PENDING");


var STATE = new Enum("CLOSED","PENDING");

STATE.CLOSED === STATUS.CLOSED  // false;
STATE.CLOSED === "CLOSED"  // false;
STATE.CLOSED.toString() === "CLOSED"  // true;

このライブラリで定義されています。https://github.com/webmodule/foo/blob/master/foo.js#L217


これが私が使用する解決策です。

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クラスにはいくつかの連想キャッシングがあります

いくつかの欠点:

  • 私が列挙型への参照を保持するので、そこではいくらか面倒なメモリ管理が行われます
  • まだ型の安全性はありません

これはほとんど答えではありませんが、個人的にはうまくいくと思います

それは、値が何であっても問題ないので(0,1,2を使っています)、現在の値を出力したい場合に備えて意味のある文字列を使います。


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

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

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

ほとんどの人の "Perfered Syntax"はすでに上にリストされています。 しかし、パフォーマンスには大きな問題があります。 上記の答えのうちの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は私がまだ見つけていない最高のものです。 オンラインアクセスはhereで見つけることができhere 。 クローズコンパイラは、この列挙データをすべて取り込んでインライン展開することができます。これにより、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);

さて、これらの列挙がないと同等のファイルがどの程度大きくなるかを見てみましょう。

これらの列挙型がないソース (length:1,973 bytes(477 bytes short!))
これらの列挙なしでは、最小化されました (長さ:843バイト(238バイト ))

見てきたように、列挙がなければ、ソースコードは、より大きい縮小コードを犠牲にして短くなります。 私はあなたについて知りません。私は最終製品にソースコードを組み込むのは嫌いですが、この列挙方法は非常に優れているためファイルサイズが小さくなっています。 それに加えて、この形式の列挙は高速です。 確かに、この列挙形式は、行く方法です。


現代のブラウザでは、 symbolプリミティブデータ型があり、列挙型の作成に使用できます。 各シンボル値がJavaScriptによって一意であることが保証されているので、列挙型の型の安全性が保証されます。つまり、 Symbol() != Symbol()です。 例えば:

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

私は私の列挙が大好きなので、これで遊んできました。 =)

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

ここにjsfiddleがある: http://jsfiddle.net/ZV4A6/ ://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属性のためwritable:falseこれ型安全にする必要があります。

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

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

TypeScript列挙型を実装するには、いくつかの方法があります。

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

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

2014年10月の時点で、現代的な解決策がここにあります。ソリューションをノードモジュールとして記述し、MochaとChaiを使用したテストとアンダースコアを使用しました。これらは簡単に無視し、必要に応じて列挙型コードを取得するだけです。

過度に畳み込まれたライブラリなどで多くの投稿が見られます。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;

そしてそれがあなたに何を与えるかを説明するテスト:

file: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を呼び出すだけですべてのキーを取得できます。また、キー自身を整数定数に一致させることもできます。また、異なる値でファクトリを再利用し、生成されたEnumをNodeのモジュラ手法を使用してエクスポートすることができます。

もう一度、カジュアルなユーザやブラウザなどでコードの一部を取り除いて、アンダースコアライブラリをコード内で使用したくない場合は削除してください。


それは使いやすいです、と私は思います。https://.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"]));


にもかかわらず、静的メソッドのみ(とない静的プロパティ)(参照ES2015でサポートされているhere不思議あなたがバベルで以下を使用することができ、§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;

私は、の混合物を使用して、しばらく前にそれを行っていた__defineGetter__し、__defineSetter__またはdefinePropertyJSのバージョンによって異なります。

ここに私が作ったenum生成関数があります:https://gist.github.com/gfarrell/6716853 : https://gist.github.com/gfarrell/6716853

あなたは次のように使います:

var Colours = Enum('RED', 'GREEN', 'BLUE');

そして、それは不変の文字列を作成します:int辞書(列挙型)。


私はちょうど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をご確認くださいhttps://github.com/greenlaw110/enumjs

アップデート

私はgen_enumパッケージを廃止し、不変オブジェクト、JSON文字列の逆シリアル化、文字列定数、ビットマップ生成など、より多くの機能を提供するconstjsパッケージに関数をconstjsます。詳細については、constjsconstjsしてください。

アップグレードするには、文を変更gen_enumするconstjsだけです

var genEnum = require('gen_enum');

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




enums