JavaScript에서 열거 형을 정의하기 위해 선호되는 구문은 무엇입니까?


2082

JavaScript에서 열거 형을 정의하기 위해 선호되는 구문은 무엇입니까? 다음과 같은 것 :

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

// later on

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

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


131
0열거 번호로 사용하지 마십시오 . 설정되지 않은 것에 사용되지 않는 한. JS는를 false || undefined || null || 0 || "" || '' || NaN사용하여 비교할 때 모두 동일한 값으로 취급 합니다 ==.
matsko

152
@matsko는 ==를 사용하는 것에 대한 논쟁이 아닙니다.
sdm350

6
0 == nullfalse를 반환
mcont

11
그러나 false == 0+null == 0(와 숫자로 전환은 때때로 당신이 그것을 기대하지 않을 때 일) 동안 null == undefined도하고 +undefined있다 NaN(하지만 NaN != NaN).
sanderd17

46
이중 평등 행렬은 Microsoft Word의 자동 서식보다 더 혼란스러워
aaaaaa

답변:


895

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

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

출처

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


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
Gabriel Llamas

34
이전 버전과의 호환성을 위해if (Object.freeze) { Object.freeze(DaysEnum); }
saluce

17
나는 ({ monday: {}, 등을하는 것이 stringify를 통해 해당 객체를 JSON으로 변환하면 [{"day": {}}]작동하지 않을 것이라는 것을 의미합니다.
jcollum

10
@Supuhstar이 질문에 대한 나의 의견은 지금 다르다. freeze ()를 사용하지 마십시오. 완전히 쓸모가없고 "멍청한"일을하는 데 시간 낭비입니다. 열거 형을 노출하려면 간단히 다음을 노출하십시오 var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. 이전 주석과 같은 객체를 비교하는 것이 숫자를 비교하는 것보다 훨씬 느립니다.
Gabriel Llamas

608

이것은 많은 대답이 아니지만 개인적으로 잘 작동한다고 말하고 싶습니다.

값이 무엇이든 상관없이 (0, 1, 2를 사용 했음) 현재 값을 출력하려는 ​​경우 의미있는 문자열을 사용합니다.


377
이것은 다른 답변에 언급되었지만이 답변이 허용되는 답변이므로 여기에 게시하겠습니다. OP의 솔루션이 맞습니다. 그러나와 함께 사용하면 더 좋습니다 Object.freeze(). 이렇게하면 다른 코드가 열거 형 값을 변경하지 못하게됩니다. 예 :var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth

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(returns true). 각 인스턴스에는 a .name().ordinal()메서드가 있으며 열거 형 자체에는 values()모든 상수의 배열을 반환하는 메서드가 있습니다.
Vivin Paliath

3
나는 "의미있는 문자열"제안에 동의하지 않습니다. 열거 형은 문자열이나 숫자로 생각해서는 안됩니다. 그것들은 추상적 데이터 타입입니다. 헬퍼 메소드가 없으면 "현재 값을 출력"할 수 없어야합니다. Java 및 .NET에서는 그 ToString()방법입니다. 우리 JS 개발자들은 이미 "일하는 것"에 너무 의존하고 있습니다! 또한 switch열거 형 을 빠르게 사용할 수 있어야합니다 . 문자열을 비교하는 것이 숫자를 비교하는 것보다 느리므로 switch정수 대신 문자열을 사용하면 성능 이 약간 떨어 집니다.
Rabadash8820

501

최신 정보

모든 지지자들에게 감사하지만 아래 답변이 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"};

열거의 필드 (이 예에서는 값, 이름 및 코드)는 신원 확인에 필요하지 않으며 단지 편의를 위해서만 있습니다. 또한 size 속성 자체의 이름을 하드 코딩 할 필요는 없지만 동적으로 설정할 수도 있습니다. 따라서 새로운 열거 형 값의 이름 만 알고 있다고 가정하면 문제없이 계속 추가 할 수 있습니다.

// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};

물론 이것은 더 이상 가정을 할 수 없다는 것을 의미합니다 (예 : 값이 올바른 순서를 나타냄)

JavaScript에서 객체는 map 또는 hash table과 같습니다 . 이름-값 쌍의 집합입니다. 미리 알지 못하고 반복하거나 조작 할 수 있습니다.

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

당신이 네임 스페이스에 관심이 있다면 그건 그렇고, 당신은 자바 스크립트에 대한 간단하지만 강력한 네임 스페이스와 의존성 관리에 대한 내 솔루션 좀보고 할 수 있습니다 : 패키지 JS를


그렇다면 이름 만 있다면 어떻게 간단하게 크기를 만들 수 있습니까?
Johanisma

2
@Johanisma : 그 유스 케이스는 열거 형에 실제로 의미가 없습니다. 그러나 나중에 Javascript에서 추가 값을 추가하지 못하게하는 것은 없습니다. 내 대답에 그 예를 추가하겠습니다.
Stijn de Witt

2
속성 접근 방식을 사용하여 게시물 링크를 +1하십시오. OP에서와 같이 기본 선언은 간단하고 원하는 경우 속성 기능이 추가되어 우아합니다.
goodeye

@Stijin은 업데이트 된 솔루션을 정말 좋아했습니다. 귀하의 블로그에 주석과 아래 주석으로 코드를 게시했습니다. 기본적으로 함수를 사용하여 기존 해시 목록에서 속성 빌드를 수행하고 선택적으로 고정합니다 (내 목록의 mkenum_2). 건배.
Andrew Philips

이를 구현하는 라이브러리도 있으며, 비교 및 ​​역 검색에 관한 훌륭한 기능을 포함합니다 : github.com/adrai/enum
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
자바 스크립트에는 형식 안전성이 있습니까?
Scott Evernden

3
따라서 값을 객체 속성에 매핑하지 마십시오. getter를 사용하여 열거 자에 액세스하십시오 (예 : "개인"오브젝트의 특성으로 저장 됨). 순진한 구현은 다음과 같습니다.var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax

2
@Scott Evernden : 점령. @ kangax : 요점은 여전히 ​​해킹이라는 것입니다. 열거 형은 단순히 자바 스크립트, 시대, 이야기의 끝에는 존재하지 않습니다. 팀 실베스터가 제안한 패턴조차도 여전히 이상적인 해킹은 아닙니다.
Randolpho

2
리터럴로 코드를 뿌리는 것은 유지 관리가 쉽지 않으므로 상수를 만드는 것이 좋습니다. 물론 Javascript에는 상수가 없습니다. 기본적으로 이것은 깨끗한 코드를 작성하는 방법입니다. 시행 할 수는 없지만 Javascript에서는 많지 않습니다. 상수, 함수 또는 대부분 무엇이든 재정의 할 수 있습니다. EG : document.getElementById = function () {alert ( "고정되었습니다. 자바 스크립트는 형식이 안전하지 않습니다.");};
Stijn de Witt

3
@ Randolpho : Artur Czajka의 Object.freeze는 어떻습니까? 월요일에 목요일로 설정하는 것을 방지하기 위해 노력하지 않습니까?
Michael-Clay는 어디에 있습니까 Shirky

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

이렇게하면 상수를 일반적인 방식으로 액세스 할 수 있으며 (예, 예, 색, 녹색) 순차적 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 2016 년

@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 ... enum 값은 종종 직렬화 가능해야하며 Symbol은 직렬화 및 역 직렬화에 편리하지 않습니다.
Andy

3
이 날이거나 Object.freeze"자신의 위험에 monkeypatch는"JS의 사회 계약이라는 사실을 인정하지 않은 사람에 대해서만?
Andy

@ 앤디 네 직렬화는 성가시다. 나는 명시를하고 결국 toJSON:이 방법을 사용하는 것이 포함하는 클래스에 stackoverflow.com/questions/58499828/...
치로 틸리冠状病毒审查六四事件法轮功

30

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

파일 크기 문제를 바로 잡겠습니다. 여기에 나열된 다른 모든 답변은 코드를 극도로 부풀립니다. 최상의 성능, 코드의 가독성, 대규모 프로젝트 관리, 많은 코드 편집기의 구문 힌트 및 축소로 코드 크기를 줄이려면 열거를 수행하는 올바른 방법 인 밑줄 표기법 변수를 제시합니다.


v v w w

밑줄 표기법 변수

위의 차트와 아래 예제에서 알 수 있듯이 시작하기 쉬운 5 단계는 다음과 같습니다.

  1. 열거 그룹의 이름을 결정하십시오. 열거의 목적 또는 열거의 항목을 설명 할 수있는 명사를 생각해보십시오. 예를 들어, 사용자가 선택할 수있는 색상을 나타내는 열거 그룹은 COLORS보다 COLORCHOICES로 명명하는 것이 좋습니다.
  2. 그룹의 열거가 상호 배타적인지 독립적인지 결정하십시오. 상호 배타적 인 경우 열거 된 각 변수 이름을로 시작하십시오 ENUM_. 독립적이거나 나란히있는 경우을 사용하십시오 INDEX_.
  3. 각 항목에 대해 이름이 ENUM_또는로 시작 INDEX_하고 그룹 이름, 밑줄, 속성의 고유 한 이름을 갖는 새 로컬 변수를 작성 하십시오.
  4. 추가 ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, 또는 INDEXLEN_(여부 LEN_또는LENGTH_ 매우 끝에 개인적인 취향) 열거 변수를. 열거 형에 추가 항목을 추가하고이 값을 늘리면 코드가 손상되지 않도록 코드에서 가능한 한이 변수를 사용해야합니다.
  5. 말은 것을이 페이지에 대한 의견이 있습니다 0에서 시작하여, 마지막으로보다 각 연속 열거 변수에 값을 부여 0열거 값 때문으로 사용해서는 안 0 == null, 0 == false, 0 == "", 및 기타 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_RATENUMLEN_PET 그에 따라 업데이트하면 됩니다. 다른 열거 형 시스템에 새 항목을 추가하는 것이 더 어려울 수 있습니다.


Wvwwvw wvwvwvw vwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

또한이 열거 구문을 사용하면 아래와 같이 명확하고 간결한 클래스 확장이 가능합니다. 클래스를 확장하려면 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 바이트)

일부는 이것이 다른 솔루션보다 실용적이지 않다고 말할 수 있습니다. 허리 톤 공간, 쓰기에 오랜 시간이 걸리며 설탕 구문으로 코팅되지 않았습니다. 코드를 축소하지 않으면 사람들이 옳을 것입니다. 그러나 합리적인 사람은 최종 제품에 축소되지 않은 코드를 남길 수 없습니다. 이 축소를 위해 클로저 컴파일러는 아직 찾지 못했습니다. 온라인 액세스는 여기 에서 찾을 수 있습니다 . 클로저 컴파일러는이 열거 데이터를 모두 가져 와서 인라인 할 수있어 자바 스크립트를 매우 작게 만들고 빠르게 듀퍼를 실행할 수 있습니다. 따라서 폐쇄 컴파일러로 최소화하십시오. 관찰하십시오.


Wvwwvw wvwvwvw vwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

클로저 컴파일러는 다른 자바 스크립트 축소 기의 용량을 넘어서는 추론을 통해 매우 놀라운 최적화를 수행 할 수 있습니다. 클로저 컴파일러는 기본 변수를 고정 값으로 인라인 할 수 있습니다. Closure Compiler는 이러한 인라인 된 값을 기반으로 추론 할 수 있으며 if 문과 루프에서 사용되지 않는 블록을 제거 할 수 있습니다.

클로저 컴파일러를 통한 코드 연결

'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는 더 축소 된 파일 크기로 조직화 된 코드를 처벌하는 반면, 많은 Minifier가 더 작은 파일 크기로 조직화 된 코드를 처벌하는 반면, Closure Compiler는 트릭을 사용하는 경우 더 작은 파일 크기를 출력하기 위해 모든 청결과 정성을 거칠 수 있기 때문에 변수 이름 열거와 같습니다. 이것이 하나의 마음에 코딩의 성배입니다. 작은 크기로 코드를 지원하고 더 나은 프로그래밍 습관을 훈련하여 마음을 돕는 도구입니다.


Wvwwvw wvwvwvw vwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

이제 열거 형이없는 등가 파일의 크기를 살펴 보겠습니다.

열거를 사용하지 않는 소스 (길이 : 1,973 바이트 (열거 된 코드보다 477 바이트 짧음!))
열거를 사용하지 않고 축소 된 (길이 : 843 바이트 ( 열거 된 코드보다 238 바이트 ))

코드 크기 차트



알 수 있듯이 열거가 없으면 축소 된 코드가 클수록 소스 코드가 짧아집니다. 나는 당신에 대해 모른다; 그러나 나는 최종 제품에 소스 코드를 포함시키지 않는다는 것을 확실히 알고 있습니다. 따라서이 형식의 열거 형은 파일 크기가 작을수록 훨씬 우수합니다.


Wvwwvw wvwvwvw vwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

이 형식의 열거에 대한 또 다른 장점은 축소 된 코드 크기를 희생하지 않고도 대규모 프로젝트를 쉽게 관리하는 데 사용할 수 있다는 것입니다. 다른 많은 사람들과 함께 대규모 프로젝트를 작업 할 때 코드를 만든 사람으로 변수 이름을 명시 적으로 표시하고 레이블을 지정하면 코드의 원래 작성자를 공동 버그 수정을 위해 빠르게 식별 할 수 있습니다.

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

열거 형이있는 코드에 일반 객체가있는 코드보다 훨씬 길어 보이는 것처럼 보일 수도 있지만 외모는 속일 수 있습니다. 서사시 폐쇄 컴파일러를 사용할 때 소스 코드 크기는 출력 크기에 비례하지 않는다는 점을 기억해야합니다. 관찰하십시오.

/// 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 vwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

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

또한이 개인 체리 는 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 vwxvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvw wvwwvw wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvww

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

이 열거 방법론은 실제로 최소화 된 코드 크기뿐만 아니라 성능, 디버깅 및 공동 작업을 수행하는 가장 좋은 방법이라고 말하는 것이 안전하다고 생각합니다.

유용한 질문을 읽은 후 질문 상자의 왼쪽 상단 화살표를 클릭하여 글에 시간을 쏟아 준 저자에게 감사합니다. 각 답변 상자에는 이러한 위쪽 화살표 중 하나가 있습니다.


뭐라고. 가독성과 사용 편의성 및 코드 크기에 대한 이해를 강력하게 선호합니다.
앤드류

1
@Andrew 내 대답으로는 둘 다 가질 수 있습니다. 내 대답은 가장 사용하기 쉬운 코드와 가장 작은 코드 크기를 사용합니다 .🙂
Jack Giffin

1
@Andrew 나는 Yet Another Enum (YEA!) 을 내 대답의 색상 파서 예제에 적용하려고 시도했습니다 . 그러나 해결해야 할 몇 가지 문제가 있습니다. YEA 는 하위 클래스를 사용하여 열거를 확장 할 수있는 방법이 없으므로 대규모 프로젝트에서 관리하기가 어려울 수있는 별도의 부모 및 자식 클래스를 만들어야합니다. YEA 는 출품작의 존재를 보장하지 않으므로 (예 : colors.REED수익률 undefined) 오타가 찾기 어려운 수수께끼를 만듭니다. YEA 는 인덱스와 ID로 열거를 사용하는 것을 구별하지 않으므로 모든 것이 동일하게 보이는 코드를 혼란스럽게 만듭니다. …
Jack Giffin

1
@Andrew… YEA는 Closure Compiler의 축소 기능을 방해합니다. YEA (3549 바이트)가있는 소스 코드를 YEA (1344 바이트)가있는 축소 코드와 내 솔루션 (604 바이트)이있는 축소 코드와 비교하십시오. 마지막으로 YEA는 문자열 이름과 열거 된 ID를 구분하기 때문에 "이름으로 매핑"과 관련이 있습니다. Mine은 ID 만 고려하므로 "이름 별 매핑"이 필요하지 않으므로 설계가 간단하고 성능이 향상됩니다. 솔루션을 공유해 주셔서 감사하지만 실용적이려면 많은 수정이 필요합니다.
잭 지핀

1
@Andrew 당신은 내가 👍
Jack 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;Enum의 끝에 추가 하면 다음을 수행 할 수 있습니다.var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

나는 그것을하는 일반적인 방법이 아니기 때문에 그것을 고려하지 않았습니다. 그러나 당신은 절대적으로 맞습니다! 이 부분을 편집하겠습니다.
Duncan

객체 공간을 넓히는 팬이 아니지만 (글로벌 함수 ENUM 사용) 나는 이것을 정말로 좋아합니다. 이것을 mkenum 함수로 변환하고 선택적 숫자 할당을 추가했습니다 => var mixedUp = mkenum ( 'BLACK', {RED : 0x0F00, BLUE : 0X0F, GREEN : 0x0F0, WHITE : 0x0FFF, ONE : 1) ; // 아래 코드로 답변을 추가하십시오. 감사.
Andrew Philips

솔직히 말해서, 나는 이것을 더 이상 사용하지 않습니다. Google의 Closure Compiler를 사용하고 있으며 고급 설정을 사용하면 제대로 작동하지 않습니다 (또는 복잡하게 만듭니다). 방금 표준 객체 표기법으로 돌아갔습니다.
던컨

1
false의 기본이다 writable, enumerable하고 configurable. 기본값을 씹을 필요가 없습니다.
16:03에

23

자바 스크립트 프록시 사용

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의 본질적인 기능을 다루지 않습니다.

아시다시피 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 {
  // 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 : 도대체 무엇입니까?

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


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
1을 반환 플러스 MyEnum [ "바"] 작품 ... <3 타이프 라이터 지금까지 ...
데이비드 Karlaš

3
물론 실제로 Typescript를 사용하고 있다면 :enum MyEnum { Foo, Bar, Foobar }
Parliament

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

3
열거을 확장하는 것은 정말 미친 소리
Codii

언어가 기본적으로 언어를 지원하지 않으면이 규칙을 유지하고 이와 같이 사용하는 것이 합리적입니다! 동의한다!
xpto

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

나는 보통 메시지 객체에서 열거 형을 매핑하기 위해 마지막 두 가지 방법을 사용합니다.

이 방법의 장점은 다음과 같습니다.

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

몇 가지 단점 :

  • 열거 형에 대한 참조를 유지하면서 지저분한 메모리 관리가 진행됩니다.
  • 여전히 타입 안전

14

객체 리터럴을 만듭니다.

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

12
const객체의 속성을 변경할 Modes수 없게 만드는 것은 변수 를 다른 것에 다시 할당 할 수 없다는 것을 의미합니다 . 더 완벽하게 Object.freeze()하려면를 함께 사용하십시오 const.
rvighne

사용하지 마십시오 Object.freeze. Closure Compiler가 객체를 인라인하지 못하게합니다.
Jack Giffin

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 나는 당신의 대답이 더 성능이 좋으며 내 메모리가 더 많은 메모리를 필요로한다는 것에 동의합니다. 다른 답변 과이 답변을 선호하는 개발자를 존중하십시오.
Xeltor

7

Andre 'Fi'의 솔루션을 수정했습니다.

  function Enum() {
    var that = this;
    for (var i in arguments) {
        that[arguments[i]] = i;
    }
    this.name = function(value) {
        for (var key in that) {
            if (that[key] == value) {
                return key;
            }
        }
    };
    this.exist = function(value) {
        return (typeof that.name(value) !== "undefined");
    };
    if (Object.freeze) {
        Object.freeze(that);
    }
  }

테스트:

var Color = new Enum('RED', 'GREEN', 'BLUE');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true

6

Java에서 열거 형을 모델로 한이 접근법을 생각해 냈습니다 . 이것들은 타입 안전하므로 수행 할 수 있습니다instanceof 검사도 있습니다.

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

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

Days이제 Days열거 형을 말합니다 .

Days.Monday instanceof Days; // true

Days.Friday.name(); // "Friday"
Days.Friday.ordinal(); // 4

Days.Sunday === Days.Sunday; // true
Days.Sunday === Days.Friday; // false

Days.Sunday.toString(); // "Sunday"

Days.toString() // "Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } "

Days.values().map(function(e) { return e.name(); }); //["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
Days.values()[4].name(); //"Friday"

Days.fromName("Thursday") === Days.Thursday // true
Days.fromName("Wednesday").name() // "Wednesday"
Days.Friday.fromName("Saturday").name() // "Saturday"

구현 :

var Enum = (function () {
    /**
     * Function to define an enum
     * @param typeName - The name of the enum.
     * @param constants - The constants on the enum. Can be an array of strings, or an object where each key is an enum
     * constant, and the values are objects that describe attributes that can be attached to the associated constant.
     */
    function define(typeName, constants) {

        /** Check Arguments **/
        if (typeof typeName === "undefined") {
            throw new TypeError("A name is required.");
        }

        if (!(constants instanceof Array) && (Object.getPrototypeOf(constants) !== Object.prototype)) {

            throw new TypeError("The constants parameter must either be an array or an object.");

        } else if ((constants instanceof Array) && constants.length === 0) {

            throw new TypeError("Need to provide at least one constant.");

        } else if ((constants instanceof Array) && !constants.reduce(function (isString, element) {
                return isString && (typeof element === "string");
            }, true)) {

            throw new TypeError("One or more elements in the constant array is not a string.");

        } else if (Object.getPrototypeOf(constants) === Object.prototype && !Object.keys(constants).reduce(function (isObject, constant) {
                return Object.getPrototypeOf(constants[constant]) === Object.prototype;
            }, true)) {

            throw new TypeError("One or more constants do not have an associated object-value.");

        }

        var isArray = (constants instanceof Array);
        var isObject = !isArray;

        /** Private sentinel-object used to guard enum constructor so that no one else can create enum instances **/
        function __() { };

        /** Dynamically define a function with the same name as the enum we want to define. **/
        var __enum = new Function(["__"],
            "return function " + typeName + "(sentinel, name, ordinal) {" +
                "if(!(sentinel instanceof __)) {" +
                    "throw new TypeError(\"Cannot instantiate an instance of " + typeName + ".\");" +
                "}" +

                "this.__name = name;" +
                "this.__ordinal = ordinal;" +
            "}"
        )(__);

        /** Private objects used to maintain enum instances for values(), and to look up enum instances for fromName() **/
        var __values = [];
        var __dict = {};

        /** Attach values() and fromName() methods to the class itself (kind of like static methods). **/
        Object.defineProperty(__enum, "values", {
            value: function () {
                return __values;
            }
        });

        Object.defineProperty(__enum, "fromName", {
            value: function (name) {
                var __constant = __dict[name]
                if (__constant) {
                    return __constant;
                } else {
                    throw new TypeError(typeName + " does not have a constant with name " + name + ".");
                }
            }
        });

        /**
         * The following methods are available to all instances of the enum. values() and fromName() need to be
         * available to each constant, and so we will attach them on the prototype. But really, they're just
         * aliases to their counterparts on the prototype.
         */
        Object.defineProperty(__enum.prototype, "values", {
            value: __enum.values
        });

        Object.defineProperty(__enum.prototype, "fromName", {
            value: __enum.fromName
        });

        Object.defineProperty(__enum.prototype, "name", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "ordinal", {
            value: function () {
                return this.__ordinal;
            }
        });

        Object.defineProperty(__enum.prototype, "valueOf", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "toString", {
            value: function () {
                return this.__name;
            }
        });

        /**
         * If constants was an array, we can the element values directly. Otherwise, we will have to use the keys
         * from the constants object.
         */
        var _constants = constants;
        if (isObject) {
            _constants = Object.keys(constants);
        }

        /** Iterate over all constants, create an instance of our enum for each one, and attach it to the enum type **/
        _constants.forEach(function (name, ordinal) {
            // Create an instance of the enum
            var __constant = new __enum(new __(), name, ordinal);

            // If constants was an object, we want to attach the provided attributes to the instance.
            if (isObject) {
                Object.keys(constants[name]).forEach(function (attr) {
                    Object.defineProperty(__constant, attr, {
                        value: constants[name][attr]
                    });
                });
            }

            // Freeze the instance so that it cannot be modified.
            Object.freeze(__constant);

            // Attach the instance using the provided name to the enum type itself.
            Object.defineProperty(__enum, name, {
                value: __constant
            });

            // Update our private objects
            __values.push(__constant);
            __dict[name] = __constant;
        });

        /** Define a friendly toString method for the enum **/
        var string = typeName + " { " + __enum.values().map(function (c) {
                return c.name();
            }).join(", ") + " } ";

        Object.defineProperty(__enum, "toString", {
            value: function () {
                return string;
            }
        });

        /** Freeze our private objects **/
        Object.freeze(__values);
        Object.freeze(__dict);

        /** Freeze the prototype on the enum and the enum itself **/
        Object.freeze(__enum.prototype);
        Object.freeze(__enum);

        /** Return the enum **/
        return __enum;
    }

    return {
        define: define
    }

})();

보기 좋을 것 freeze입니다. 이전 버전과의 호환성을위한 방법 이 있는지 확인해야 합니까? 예 :if (Object.freeze) { Object.freeze(values); }
FBB

좋은 지적! 할 것이다!
Vivin Paliath

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

마지막은 문자열에 가장 효율적으로 보이며이 데이터를 교환하는 서버 및 클라이언트가있는 경우 총 대역폭을 줄입니다.
물론, 데이터에 충돌이 없는지 확인해야합니다 (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: {}
}

이런 식으로 다른 열거 형 값에 중복 숫자를 할당하지 않아도됩니다. 새 객체가 인스턴스화되어 모든 열거 형 값에 할당됩니다.


이 답변은 과소 평가되었습니다. 단순성에있어 제가 가장 좋아하는 아이디어 중 하나입니다. 실제로, 나는 지금 디버깅하기가 더 쉽기 때문에 문자열을 고수 할 것이라고 생각합니다.
Domino

흠,이 코드가 두 번 호출되지 않도록하십시오.
Andrew

4

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

매우 영리하고 감사합니다
Ilan

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 

이 작은 도구에 대한 한 가지 좋은 점은 현대 환경 (nodejs 및 IE 9 + 브라우저 포함)에 있습니다. 반환 된 Enum 객체는 변경할 수 없습니다.

자세한 내용은 https://github.com/greenlaw110/enumjs 를 확인 하십시오.

업데이트

gen_enum패키지를 더 이상 사용하지 않고 함수를 constjs 패키지에 병합하면 변경 불가능한 객체, JSON 문자열 역 직렬화, 문자열 상수 및 비트 맵 생성 등 더 많은 기능을 제공합니다. Checkout 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" 다른 열거 형이 아닌 enum "(일반 객체 또는 기호를 사용하는 경우 구분할 수 없음)입니다.

최신 정보

위의 코드는 고정 된 추가 객체를 만들 수 있다는 단점이 있습니다 CellState( CellState동결 된 이후 정적 필드에 객체를 할당 할 수는 없지만 ). 그럼에도 불구하고 아래의 더 세련된 코드는 다음과 같은 장점을 제공합니다.

  1. 더 이상 유형의 객체가 없음 CellState 만들 수 없습니다
  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 22.44에

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가 그것을 enumJavascript로 번역하는 방법입니다 .

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]returns 가 왜 혼란 스러웠지만 'Active', 죽은 간단한 -Assignment 연산자가 값을 할당하고 값을 반환 한다는 것을 깨달았 습니다.

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

3

작성일 현재 2014 년 10 월 -여기에 현대적인 솔루션이 있습니다. 솔루션을 노드 모듈로 작성 중이며 밑줄 JS뿐만 아니라 Mocha 및 Chai를 사용한 테스트가 포함되었습니다. 쉽게 무시할 수 있으며 원하는 경우 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를 호출하여 모든 키를 얻을 수 있으며 키 자체를 정수 상수와 일치시킬 수 있습니다. 또한 다른 값으로 팩토리를 재사용하고 노드의 모듈 방식을 사용하여 생성 된 Enum을 내보낼 수 있습니다.

다시 한 번, 일반 사용자이거나 브라우저 등에서 코드의 팩토리 부분을 가져 가면 코드에서 밑줄 라이브러리를 사용하지 않을 가능성도 있습니다.


5
"공장, 밑줄 또는 공상이 아닌 열거 형을 원하는 일반 사용자로서 어떻게 할 수 있습니까?"라는 답변 만 게시 할 수 있습니까?
GreenAsJade

5
이것은 개발자의 관점에서 볼 때 매우 훌륭하지만 매우 깨끗하거나 읽을 수는 없습니다. OP의 Enum 솔루션은 모든면에서 더 쉽고 읽기 쉬우므로 사용하기가 더 좋습니다. 아직도, 당신이 이것을 생각해 낸 것이 정말 끔찍합니다.
David

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.