javascript - react - 타입스크립트 enum




자바 스크립트에서 enum을 정의하기위한 기본 구문은 무엇입니까? (20)

자바 스크립트에서 enum을 정의하기위한 기본 구문은 무엇입니까? 같은 것 :

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

// later on

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

아니면 더 바람직한 관용어가 있습니까?


Javascript Proxies 사용

TLDR :이 클래스를 유틸리티 메서드에 추가하고 코드 전체에서 사용하면 기존 프로그래밍 언어의 열거 형 동작을 모방하고 존재하지 않는 열거자를 액세스하려고하면 실제로 오류가 발생합니다.

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

그런 다음 클래스를 인스턴스화하여 열거 형을 만듭니다.

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

전체 설명 :

전통적인 언어에서 얻은 Enum의 한 가지 매우 유용한 기능은 존재하지 않는 열거자를 액세스하려고 시도 할 때 컴파일 타임 오류가 발생한다는 것입니다.

추가 값이 실수로 / 악의적으로 추가되는 것을 방지하기 위해 조롱 된 enum 구조를 동결시키는 것 외에도 다른 답변에서는 Enum의 고유 기능을 처리하지 못합니다.

JavaScript에서 존재하지 않는 멤버에 액세스하면 undefined 가 반환되고 코드가 날아 가지 않습니다. 열거자는 미리 정의 된 상수 (요일)이므로 열거자를 정의하지 않아야합니다.

저를 잘못 이해하지 마십시오. 정의되지 않은 속성에 액세스 할 때 undefined 를 반환하는 JavaScript의 동작은 실제로 언어의 매우 강력한 기능이지만, 전통적인 Enum 구조를 모의하려고 할 때 원하는 기능이 아닙니다.

이것은 프록시 객체가 빛나는 곳입니다. 프록시는 ES6 (ES2015)의 도입으로 언어로 표준화되었습니다. MDN의 설명은 다음과 같습니다.

Proxy 객체는 기본 작업 (예 : 속성 조회, 할당, 열거, 함수 호출 등)에 대한 사용자 정의 동작을 정의하는 데 사용됩니다.

웹 서버 프록시와 마찬가지로 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 : 도대체 프록시 란 무엇입니까?

나는 언제 어디서나 프록시라는 단어를 처음 보았을 때, 오랫동안 내게 의미가 없었던 것을 기억합니다. 지금 당장이라면 프록시를 일반화하는 쉬운 방법은 소프트웨어, 기관 또는 두 서버, 회사 또는 사람들 사이의 중개자 또는 중개자 역할을하는 사람으로 생각하는 것입니다.


1.8.5부터 물체를 봉인하고 동결시킬 수 있으므로 위의 내용을 다음과 같이 정의하십시오.

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

또는

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

and voila! JS enums.

그러나 이것은 종종 열거 형의 주요 목표 인 변수에 원하지 않는 값을 할당하는 것을 방지하지 않습니다.

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

열거 형 또는 다른 방법을 사용하여보다 강력한 유형 안전성을 보장하는 한 가지 방법은 TypeScript 또는 Flow 와 같은 도구를 사용하는 것입니다.

Source

따옴표는 필요하지 않지만 일관성을 유지했습니다.


IE8은 freeze () 메소드를 지원하지 않습니다.
출처 : http://kangax.github.io/compat-table/es5/ , "Show obsolete browsers?"를 클릭하십시오. 상단에 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>..."

결론 : 당신은 할 수 없습니다.

가짜로 만들 수는 있지만 유형 안전을 얻지는 못합니다. 일반적으로 정수 값에 매핑 된 문자열 값의 간단한 사전을 만들어이 작업을 수행합니다. 예 :

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

그것이 열거 형의 모든 유효한 사용에 아마도 맞지는 않지만, 아주 먼 길을 간다.


나는 열심히 사랑한다. 나는 이것으로 놀고있다. =)

Object.defineProperty 사용 Object.defineProperty 나는 다소 실용적인 해결책을 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 속성으로 인해 writable:false 설정하면 유형이 안전합니다.

따라서 사용자 정의 객체를 만든 다음 Enum() 을 호출 할 수 있어야합니다. 할당 된 값은 0에서 시작하여 항목 당 증가합니다.

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

당신의 대답은 너무 복잡합니다.

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

대부분의 사람들이 "Perfered Syntax"를 이미 위에 나열했습니다. 그러나 성능에있어 가장 중요한 문제가 있습니다. 위의 답변 중 단 하나도 매우 성능이 뛰어나며 코드 크기가 극단적으로 팽창합니다. 실제 성능, 코드 읽기 용이성 및 축소로 코드 크기가 전례없이 감소한 경우 이것이 열거를 수행하는 올바른 방법입니다.

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 . 클로저 컴파일러는이 열거 데이터를 모두 가져 와서 인라인 할 수 있으므로 자바 스크립트가 빠르게 실행되고 초소형으로 작아집니다. 준수하십시오.

(길이 : 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 바이트 더 길어짐 )).

알 수 있듯이 열거 형이 없으면 소스 코드는 더 작은 축소 코드를 사용하면 더 짧아집니다. 나는 당신에 대해 모른다. 최종 제품에 소스 코드를 통합하는 것을 싫어한다.이 열거 방법이 훨씬 뛰어나므로 파일 크기가 작아진다. 이 형식의 열거 형 방식이 더 빠릅니다. 사실,이 열거 형은 갈 길입니다.


우리 모두가 원하는 것은 다음과 같습니다.

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

이렇게하면 상수를 일반적인 방법 (예 : 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');

이것은 내가 알고있는 오래된 것입니다. 그러나 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] 을 모두 검색 할 수 있습니다.


이것이 제가 사용하는 해결책입니다.

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

보통 메시지 객체의 enum을 매핑하는 마지막 두 가지 방법을 사용합니다.

이 접근법에 대한 몇 가지 장점 :

  • enum을 선언하기 쉽다.
  • 열거 형에 쉽게 액세스 할 수 있습니다.
  • 열거 형은 복합 유형 일 수 있습니다.
  • Enum 클래스에는 getByValue를 많이 사용하는 경우 연관 캐싱이 있습니다.

몇 가지 단점 :

  • 열거 형에 대한 참조를 유지하면서 거기에서 계속되는 어수선한 메모리 관리
  • 아직 유형 안전 없음

Backbone 사용하는 경우 Backbone 사용하여 열거 형 열거 기능 (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 Status = Object.freeze({
    "Connecting":0,
    "Ready":1,
    "Loading":2,
    "Processing": 3
});

가치를 얻으십시오.

console.log(Status.Ready) // 1

키 가져 오기

console.log(Object.keys(Status)[Status.Ready]) // Ready

업데이트 : 모든 upvotes 모두 주셔서 감사하지만, 아래의 내 대답은 더 이상 자바 스크립트에서 열거 형을 작성하는 가장 좋은 방법이라고 생각하지 않습니다. 자세한 내용은 내 블로그 게시물을 참조하십시오 : 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"};

열거 형 필드 (이 예제에서 값, 이름 및 코드)는 신원 확인에 필요하지 않으며 편리함을 위해서만 존재한다는 것을 기억하십시오. 또한 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.        
  }
} 

그리고 네가 네임 스페이스에 관심이 있다면 간단하지만 강력한 네임 스페이스와 자바 스크립트에 대한 의존성 관리를 위해 내 솔루션을 살펴보고 싶을 것이다. Packages JS


JS 버전에 따라 __defineGetter____defineSetter__또는 의 혼합을 사용하여 얼마 전에 작업을 수행했습니다 defineProperty.

다음은 내가 만든 enum 생성 함수입니다 : https://gist.github.com/gfarrell/6716853

당신은 이것을 다음과 같이 사용할 것입니다 :

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

그리고 그것은 불변의 문자열을 만들 것입니다 : int 사전 (열거 형).


Java에서 열거 형을 모델로 this 접근법을 생각해 냈습니다 . 이것들은 타입 - 세이프 (type-safe)이기 때문에 instanceof검사도 수행 할 수 있습니다.

열거 형을 다음과 같이 정의 할 수 있습니다.

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

Days이제 Daysenum을 참조하십시오 .

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
    }

})();

다음은 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


그리고 문자열을 사용하여 enum을 만드는 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

방금 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 

이 작은 도구에 대한 좋은 점 중 하나는 현대 환경 (nodejs 및 IE 9+ 브라우저 포함)에서 반환 된 Enum 객체가 변경 불가능하다는 것입니다.

자세한 내용은 https://github.com/greenlaw110/enumjs 확인 https://github.com/greenlaw110/enumjs

업데이트

필자는 gen_enum패키지를 폐기 하고 불변 객체, JSON 문자열 역 직렬화, 문자열 상수 및 비트 맵 생성 등을 포함한 더 많은 기능을 제공하는 constjs 패키지 로 함수를 병합합니다 . 자세한 정보는 constjs 를 참조하십시오.

에서 업그레이드하려면 gen_enumconstjs그냥 문을 변경

var genEnum = require('gen_enum');

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

사용하기 쉽습니다. 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"]));


이것은 Typescript enum가 자바 스크립트로 변환하는 방법입니다 .

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






enums