Qual é a sintaxe preferida para definir enumerações em JavaScript?


2082

Qual é a sintaxe preferida para definir enumerações em JavaScript? Algo como:

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

// later on

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

Ou existe um idioma mais preferível?


132
Não use 0como um número de enumeração. A menos que seja usado para algo que não foi definido. JS trata false || undefined || null || 0 || "" || '' || NaNtodos com o mesmo valor quando comparado ao uso ==.
precisa saber é

153
@matsko não é apenas um argumento contra o uso de ==?
Sdm350

6
0 == nullretorna false
mcont 3/04

11
Mas false == 0e +null == 0(e as conversões para números acontecem às vezes quando você não espera), enquanto null == undefinedtambém e +undefinedé NaN(embora NaN != NaN).
sanderd17

46
A matriz dupla igualdade é mais confuso do que de formatação de auto microsoft da palavra
aaaaaa

Respostas:


896

Desde a versão 1.8.5, é possível selar e congelar o objeto, então defina o acima como:

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

ou

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

e pronto! JS enums.

No entanto, isso não impede que você atribua um valor indesejado a uma variável, que geralmente é o principal objetivo das enumerações:

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

Uma maneira de garantir um grau mais alto de segurança de tipo (com enumerações ou outras) é usar uma ferramenta como TypeScript ou Flow .

Fonte

As citações não são necessárias, mas eu as guardei por consistência.


6
Segundo a Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ), é aplicável ao Firefox 4, IE 9, Opera 11.60 e sei que funciona no Chrome.
Artur Czajka

77
Esta é a resposta agora em 2012. Mais simples: var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });. Você não precisa especificar um ID, basta usar um objeto vazio para comparar enumerações. if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas

34
Para compatibilidade com versões anteriores,if (Object.freeze) { Object.freeze(DaysEnum); }
saluce

17
Eu gostaria de salientar que fazer ({ monday: {}, etc. significa que, se você converter esse objeto para JSON via stringify, obterá o [{"day": {}}]que não vai funcionar.
Jcollum #

10
@Supuhstar Minha opinião sobre esta questão agora é diferente. Não use freeze (), é completamente inútil e uma perda de tempo fazendo coisas "estúpidas". Se você deseja expor um enum, simplesmente expor o seguinte: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}. Comparar objetos como no meu comentário anterior é MUITO MAIS LENTO do que comparar números.
Gabriel Llamas

608

Isso não é muita resposta, mas eu diria que funciona muito bem, pessoalmente

Dito isto, já que não importa quais são os valores (você usou 0, 1, 2), eu usaria uma sequência significativa caso você desejasse exibir o valor atual.


377
Isso foi afirmado em outra resposta, mas como essa resposta é aceita, postarei aqui. A solução do OP está correta. Será ainda melhor, se usado com Object.freeze(). Isso impedirá que outro código altere os valores da enumeração. Exemplo:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth 16/01

5
@ TolgaE obrigado por essa biblioteca! Ele me inspirou não apenas resumindo ao mínimo, mas também adicionando alguns recursos! Eu bifurcada seu e colocar tudo aqui: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar

3
@Supuhstar Isso é ótimo! Estou feliz que você poderia usá-lo .. Sinta-se livre para fazer uma solicitação de recebimento se você queria se fundiram nesta biblioteca, então eu posso atualizar a biblioteca npm
Tolga E

2
Se alguém estiver interessado, eu implementei enums de segurança de tipo semelhantes à maneira como elas são em Java. Isso significa que você pode fazer instanceofverificações. Por exemplo ColorEnum.RED instanceof ColorEnum(retornos true). Você também pode resolver uma instância sem um nome ColorEnum.fromName("RED") === ColorEnum.RED(retornos true). Cada exemplo também tem um .name()e um .ordinal()método, e a própria enum tem um values()método que returnd uma matriz de todas as constantes.
Vivin Paliath

3
Não tenho certeza se concordo com a sugestão "string significativa". Enums não devem ser pensados ​​como seqüências de caracteres ou números; eles são tipos de dados abstratos. Não deve ser possível "gerar o valor atual" sem algum método auxiliar. Em Java e .NET, é o ToString()método. Nós, desenvolvedores JS, já somos muito dependentes de coisas "apenas funcionando"! Além disso, um deve ser capaz de rapidamente switchem um enum. A comparação de strings é mais lenta que a comparação de números; portanto, você terá um switchdesempenho um pouco pior se usar strings em vez de números inteiros.
precisa

501

ATUALIZAR

Obrigado por todos os votos positivos, mas não acho que minha resposta abaixo seja a melhor maneira de escrever enums em JavaScript. Veja minha postagem no blog para obter mais detalhes: Enums em JavaScript .


Alertar o nome já é possível:

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

Como alternativa, você pode criar objetos de valores, para que você possa comer o bolo e comê-lo também:

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

No JavaScript, como é uma linguagem dinâmica, é possível adicionar valores de enumeração ao conjunto posteriormente:

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

Lembre-se de que os campos da enumeração (valor, nome e código neste exemplo) não são necessários para a verificação de identidade e existem apenas por conveniência. Além disso, o nome da propriedade size não precisa ser codificado, mas também pode ser definido dinamicamente. Supondo que você saiba apenas o nome do seu novo valor de enumeração, você ainda pode adicioná-lo sem problemas:

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

Obviamente, isso significa que algumas suposições não podem mais ser feitas (esse valor representa a ordem correta para o tamanho, por exemplo).

Lembre-se, no JavaScript, um objeto é como um mapa ou tabela de hash . Um conjunto de pares nome-valor. Você pode percorrê-los ou manipulá-los sem saber muito sobre eles com antecedência.

Exemplo

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

E, a propósito, se você estiver interessado em espaços para nome, talvez queira dar uma olhada na minha solução para gerenciamento de espaço para nome e dependência simples mas poderoso para JavaScript: JS de pacotes


então como você criaria simplesmente um TAMANHO se você tivesse apenas o nome?
Johanisma

2
@ Johanisma: Esse caso de uso não faz muito sentido para enumerações, pois toda a idéia delas é que você conhece todos os valores antecipadamente. No entanto, não há nada que o impeça de adicionar valores extras posteriormente em Javascript. Vou adicionar um exemplo disso à minha resposta.
Stijn de Witt

2
+1 no link para sua postagem com a abordagem de propriedades. Elegante, porque as declarações básicas são simples, como no OP, com propriedades adicionais quando desejado.
goodeye

@Stijin, realmente gostei da sua solução atualizada. Código publicado nos comentários do seu blog e como um comentário abaixo. Basicamente, usando uma função, execute as propriedades construídas a partir de uma lista de hash existente e, opcionalmente, congele-a (mkenum_2 na minha lista). Felicidades.
Andrew Philips

Há também uma biblioteca que implementa-lo, incluindo também características interessantes como a comparação e de pesquisa inversa: github.com/adrai/enum
Roman M

83

Conclusão: você não pode.

Você pode fingir, mas não terá segurança de digitação. Normalmente, isso é feito criando um dicionário simples de valores de sequência mapeados para valores inteiros. Por exemplo:

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}

Document.Write("Enumerant: " + DaysEnum.tuesday);

O problema com esta abordagem? Você pode redefinir acidentalmente seu enumerador ou acidentalmente duplicar valores de enumerador. Por exemplo:

DaysEnum.monday = 4; // whoops, monday is now thursday, too

Editar

E o Object.freeze de Artur Czajka? Isso não funcionaria para impedir que você definisse segunda-feira a quinta-feira? - Quad Fry

Absolutamente, Object.freezeresolveria totalmente o problema do qual reclamei. Gostaria de lembrar a todos que, quando escrevi o texto acima, Object.freezerealmente não existia.

Agora .... agora abre algumas possibilidades muito interessantes.

Edit 2
Aqui está uma biblioteca muito boa para criar enums.

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

Embora provavelmente não sirva para todos os usos válidos de enumerações, é muito longo.


103
existe segurança de tipo em javascript?
Scott Evernden

3
Portanto, não mapeie valores para propriedades do objeto. Use getter para acessar o enumerante (armazenado como uma propriedade de, por exemplo, objeto "privado"). Uma implementação ingênua seria parecida comvar daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
#

2
@ Scott Evernden: ponto tomado. @ kangax: o ponto é que ainda é um hack. Enums simplesmente não existem em Javascript, ponto final, fim da história. Mesmo o padrão sugerido por Tim Sylvester ainda é um hack menos do que ideal.
Randolpho

2
A aspersão do código com literais não é muito sustentável, portanto faz sentido criar constantes para ele. Claro que o Javascript também não tem constantes. Então, basicamente, essa é apenas uma maneira de escrever código limpo. Ele não pode ser aplicado, mas não muito em Javascript. Você pode redefinir constantes, funções ou praticamente qualquer coisa. EG: document.getElementById = function () {alert ("Você está ferrado. Javascript não é seguro.");};
Stijn de Witt

3
@Randolpho: E o Object.freeze de Artur Czajka? Isso não funcionaria para impedir que você definisse segunda-feira a quinta-feira?
Michael - Onde está Clay Shirky

56

Aqui está o que todos nós queremos:

function Enum(constantsList) {
    for (var i in constantsList) {
        this[constantsList[i]] = i;
    }
}

Agora você pode criar suas enumerações:

var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);

Ao fazer isso, as constantes podem ser acessadas da maneira usual (YesNo.YES, Color.GREEN) e elas obtêm um valor int sequencial (NO = 0, YES = 1; RED = 0, VERDE = 1, AZUL = 2).

Você também pode adicionar métodos, usando 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 */
};


Editar - pequena melhoria - agora com varargs: (infelizmente não funciona corretamente no IE: S ... deve ficar com a versão anterior)

function Enum() {
    for (var i in arguments) {
        this[arguments[i]] = i;
    }
}

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');

Ame a simplicidade desta resposta!
Marquizzo 19/06/19

@Marquizzo (e OP) Criei uma versão aprimorada com base nesta resposta: stackoverflow.com/a/60309416/1599699
Andrew

53

Na maioria dos navegadores modernos, existe um tipo de dados primitivo de símbolo que pode ser usado para criar uma enumeração. Isso garantirá a segurança de tipo da enumeração, pois cada valor de símbolo é garantido pelo JavaScript como único, ou seja Symbol() != Symbol(). Por exemplo:

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

Para simplificar a depuração, você pode adicionar uma descrição aos valores da enumeração:

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

Demonstração Plunker

No GitHub, você pode encontrar um wrapper que simplifica o código necessário para inicializar a enumeração:

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

Esta é a resposta correta na teoria. Na prática, o suporte ao navegador de 2015 está longe de ser suficiente. Não está pronta a produção de longe.
vbraun

1
Embora o suporte ao navegador ainda não esteja disponível, esta é a melhor resposta, pois está próximo do que Symbolse destina.
rvighne

2
Os valores Meh ... enum geralmente precisam ser serializados, e os Símbolos não são tão úteis para serializar e desserializar.
217 Andy

3
Sou apenas eu ou é Object.freezeapenas para pessoas que não aceitaram o fato de "monkeypatch por seu próprio risco" ser o contrato social da JS?
Andy

@ Andy sim, a serialização é irritante. Acabei fazendo um explícito toJSONna classe que o continha para usar esta abordagem: stackoverflow.com/questions/58499828/…
Ciro Santilli escreveu:

30

𝗣𝗹𝗮𝗶𝗻 𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦 𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲

Vamos direto ao problema: tamanho do arquivo. Todas as outras respostas listadas aqui incham seu código ao extremo. Apresento a você que, para o melhor desempenho possível, legibilidade do código, gerenciamento de projetos em larga escala, dicas de sintaxe em muitos editores de código e redução no tamanho do código por minificação, esta é a maneira correta de fazer enumerações: variáveis ​​de notação sublinhada.


wvwvwvwvwwwwwwwwwwwwvwvwvwvwvwvwvwvwvwvwvwwwvvvvvvvvvvvvvvvvv

Variáveis ​​de notação sublinhada

Conforme demonstrado na tabela acima e no exemplo abaixo, aqui estão cinco etapas fáceis para começar:

  1. Determine um nome para o grupo de enumeração. Pense em um substantivo que possa descrever o objetivo da enumeração ou pelo menos as entradas na enumeração. Por exemplo, um grupo de enumerações representando cores escolhidas pelo usuário pode ser melhor denominado COLORCHOICES do que COLORS.
  2. Decida se as enumerações no grupo são mutuamente exclusivas ou independentes. Se mutuamente exclusivo, inicie cada nome de variável enumerado com ENUM_. Se independente ou lado a lado, use INDEX_.
  3. Para cada entrada, crie uma nova variável local cujo nome comece com ENUM_ou INDEX_, então o nome do grupo, um sublinhado e um nome amigável exclusivo para a propriedade
  4. Adicionar um ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, ou INDEXLEN_(se LEN_ou LENGTH_é preferência pessoal) variável enumerados no final. Você deve usar essa variável sempre que possível em seu código para garantir que adicionar uma entrada extra à enumeração e incrementar esse valor não interrompa seu código.
  5. Dê a cada variável enumerado sucessiva um valor um a mais que o último, começando em 0. Há comentários sobre esta página que digamos 0não deve ser usado como um valor enumerado porque 0 == null, 0 == false, 0 == "", e outra loucura JS. Eu submeto a você que, para evitar esse problema e aumentar o desempenho ao mesmo tempo, sempre use ===e nunca deixe ==aparecer no seu código, exceto com typeof(ex typeof X == "string"). Em todos os meus anos de uso ===, nunca tive problemas com o uso de 0 como valor de enumeração. Se você ainda é sensível, 1pode ser usado como o valor inicial em ENUM_enumerações (mas não em INDEX_enumerações) sem penalidade de desempenho em muitos casos.
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
}

Aqui está como eu lembro quando usar INDEX_e quando usar ENUM_:

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

No entanto, ENUM_em determinadas circunstâncias, pode ser apropriado como um índice, como na contagem das ocorrências de cada item.

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

Observe que, no código acima, é realmente fácil adicionar um novo tipo de animal de estimação: você apenas precisa acrescentar uma nova entrada depois ENUM_PET_RATe atualizar de ENUMLEN_PETacordo. Pode ser mais difícil e complicado adicionar uma nova entrada em outros sistemas de enumeração.


wvwwvw wvwvwvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvwwvwwwwwvvvwvv

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

Além disso, essa sintaxe de enumerações permite a extensão clara e concisa da classe, conforme mostrado abaixo. Para estender uma classe, adicione um número incremental à LEN_entrada da classe pai. Em seguida, termine a subclasse com sua própria LEN_entrada, para que a subclasse possa ser estendida ainda mais no futuro.

Diagrama de extensão de adição

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

(Comprimento: 2.450 bytes)

Alguns podem dizer que isso é menos prático do que outras soluções: consome muito espaço, leva muito tempo para escrever e não é revestido com a sintaxe do açúcar. Essas pessoas estariam certas se não reduzissem seu código. No entanto, nenhuma pessoa razoável deixaria código não minificado no produto final. Para esta minificação, o Closure Compiler é o melhor que ainda tenho para encontrar. O acesso online pode ser encontrado aqui . O compilador de fechamento é capaz de pegar todos esses dados de enumeração e incorporá-los, tornando seu Javascript super super pequeno e executando super super rápido. Portanto, Minimize com o Closure Compiler. Observar.


wvwwvw wvwvwvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvwwvwwwwwvvvwvv

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

O compilador de fechamento é capaz de executar algumas otimizações bastante incríveis por meio de inferências que estão muito além das capacidades de qualquer outro minificador de Javascript. O Closure Compiler é capaz de alinhar variáveis ​​primitivas definidas para um valor fixo. O Closure Compiler também é capaz de fazer inferências com base nesses valores embutidos e eliminar blocos não utilizados nas instruções if e loops.

Torcer código através do Closure Compiler

'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);

(Comprimento: 605 bytes)

O Closure Compiler recompensa você por codificar de forma mais inteligente e organizar bem o seu código, porque, enquanto muitos minificadores punem o código organizado com um tamanho de arquivo minificado maior, o Closure Compiler pode filtrar toda a sua limpeza e sanidade para gerar um tamanho de arquivo ainda menor se você usar truques como enumerações de nomes de variáveis. Esse, nessa única mente, é o santo graal da codificação: uma ferramenta que ajuda seu código com um tamanho reduzido menor e ajuda sua mente treinando melhores hábitos de programação.


wvwwvw wvwvwvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvwwvwwwwwvvvwvv

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

Agora, vamos ver o tamanho do arquivo equivalente sem nenhuma dessas enumerações.

Origem sem usar enumerações (comprimento: 1.973 bytes (477 bytes a menos que o código enumerado!))
Minificado sem usar enumerações (comprimento: 843 bytes (238 bytes a mais que o código enumerado ))

Tabela de tamanhos de código



Como visto, sem enumerações, o código fonte é mais curto ao custo de um código minificado maior. Não sei sobre você; mas tenho certeza de que não incorporo o código-fonte ao produto final. Portanto, essa forma de enumerações é muito superior, pois resulta em tamanhos menores de arquivos minificados.


wvwwvw wvwvwvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvwwvwwwwwvvvwvv

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

Outra vantagem sobre essa forma de enumeração é que ela pode ser usada para gerenciar facilmente projetos de grande escala sem sacrificar o tamanho reduzido do código. Ao trabalhar em um projeto grande com muitas outras pessoas, pode ser benéfico marcar e rotular explicitamente os nomes das variáveis ​​com quem criou o código, para que o criador original do código possa ser rapidamente identificado para a correção colaborativa de erros.

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

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

Além disso, essa forma de enumeração também é muito mais rápida após a minificação. Nas propriedades nomeadas normais, o navegador precisa usar hashmaps para procurar onde a propriedade está no objeto. Embora os compiladores JIT armazenem em cache esse local de maneira inteligente no objeto, ainda há uma enorme sobrecarga devido a casos especiais, como a exclusão de uma propriedade mais baixa do objeto.

Mas, com matrizes PACKED_ELEMENTS indexadas a números inteiros não esparsos , o navegador pode ignorar grande parte dessa sobrecarga, porque o índice do valor na matriz interna já foi especificado. Sim, de acordo com o padrão ECMAScript, todas as propriedades devem ser tratadas como seqüências de caracteres. No entanto, esse aspecto do padrão ECMAScript é muito enganador quanto ao desempenho, porque todos os navegadores possuem otimizações especiais para índices numéricos em matrizes.

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

Compare o código acima com o código abaixo.

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

Pode-se objetar ao código com enumerações que parecem muito mais longas do que o código com objetos comuns, mas a aparência pode enganar. É importante lembrar que o tamanho do código-fonte não é proporcional ao tamanho da saída ao usar o épico Closure Compiler. Observar.

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

O código minificado sem enumerações está acima e o código minificado com enumerações está abaixo.

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

O exemplo acima demonstra que, além de ter um desempenho superior, o código enumerado também resulta em um tamanho de arquivo minificado menor.


wvwwvw wvwvwvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvwwvwwwwwvvvwvv

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

Além disso, a cereja pessoal dessa pessoa no topo está usando essa forma de enumerações junto com o editor de texto CodeMirror no modo Javascript. O modo de destaque da sintaxe Javascript do CodeMirror destaca variáveis ​​locais no escopo atual. Dessa forma, você saberá instantaneamente quando digitar um nome de variável corretamente, porque se o nome da variável tiver sido declarado anteriormente com a varpalavra - chave, o nome da variável ficará com uma cor especial (ciano por padrão). Mesmo se você não usar o CodeMirror, pelo menos o navegador exibirá uma ajuda[variable name] is not definedexceção ao executar código com nomes de enumeração digitados incorretamente. Além disso, ferramentas JavaScript, como JSLint e Closure Compiler, falam muito alto quando você digita incorretamente o nome de uma variável de enumeração. O CodeMirror, o navegador e várias ferramentas Javascript juntas tornam a depuração dessa forma de enumeração muito simples e muito fácil.

Demonstração de destaque do 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
}

No trecho acima, você foi alertado com um erro porque ENUM_COLORENUM_DNEnão existe.


wvwwvw wvwvwvw wvwvwv vwvwvw wvwvvw wvwwvw wvwvwvw wvwvw wvwvwv vwvxwvw wvwvvwwvwwwwwvvvwvv

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻 ☑

Eu acho seguro dizer que essa metodologia de enumeração é realmente a melhor maneira de seguir não apenas o tamanho reduzido do código, mas também o desempenho, a depuração e a colaboração.

Depois de ler uma pergunta útil, agradeço ao autor por dedicar tempo à sua escrita clicando na seta superior esquerda na caixa de perguntas. Cada caixa de resposta também possui uma dessas setas para cima.


Eh. Prefiro legibilidade, facilidade de uso e compreensão ao tamanho do código.
Andrew

1
@ Andrew Com a minha resposta, você pode ter os dois. Minha resposta resulta no código mais fácil de usar / gerenciar e no menor tamanho de código minificado.🙂
Jack Giffin

1
@ Andrew Tentei aplicar o seu Yet Another Enum (YEA!) Ao exemplo do analisador de cores na minha resposta. No entanto, encontrei vários problemas que você pode querer solucionar. O YEA não tem como estender enumerações com subclasses, forçando-me a criar classes pai e filho separadas, o que pode ser bastante difícil de gerenciar em grandes projetos. O YEA não garante que a entrada exista (ex colors.REEDrendimentos undefined), portanto, erros de digitação criam enigmas indescritíveis. O YEA não distingue entre o uso de enumerações como índices e IDs, levando a códigos confusos onde tudo parece igual. …
Jack Giffin

1
@Andrew… YEA dificulta a capacidade de compactação do Closure Compiler. Compare o código-fonte com YEA (3549 bytes) ao código minificado com YEA (1344 bytes) com o código minificado com minha solução (604 bytes). Por fim, o YEA envolve "mapeamento por nome" porque separa os nomes das strings dos IDs enumerados. O meu apenas considera o ID, portanto, não é necessário "mapeamento por nome", resultando em design mais simples e melhor desempenho. Obrigado por compartilhar sua solução, mas ela precisa de muitas correções para que possa ser prática.
Jack Giffin

1
@ Andrew Você tem direito a sua opinião como a minha 👍
Jack Giffin

23

Eu tenho brincado com isso, porque eu amo meus enums. =)

Usando Object.definePropertyeu acho que surgiu uma solução um tanto viável.

Aqui está um jsfiddle: http://jsfiddle.net/ZV4A6/

Utilizando este método, você deve (em teoria) ser capaz de chamar e definir valores de enumeração para qualquer objeto, sem afetar outros atributos desse objeto.

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

Por causa do atributo, writable:falseisso deve torná-lo seguro.

Portanto, você poderá criar um objeto personalizado e, em seguida, invocá Enum()-lo. Os valores atribuídos começam em 0 e incrementam por item.

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

3
Se você adicionar return this;no final de Enum, você poderá fazer:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

Eu não considerei isso, pois não é o meu método normal de fazer as coisas. Mas você está absolutamente correto! Vou editar isso dentro.
Duncan

Eu realmente gosto disso, apesar de não ser um grande fã de estragar o espaço do objeto (com a função global ENUM). Convertido isso em uma função mkenum e adicionado atribuições numéricas opcionais => var mixedUp = mkenum ('PRETO', {VERMELHO: 0x0F00, AZUL: 0X0F, VERDE: 0x0F0, BRANCO: 0x0FFF, ONE: 1}, DOIS, TRÊS, QUATRO) ; // Adicionando meu código como resposta abaixo. Obrigado.
Andrew Philips

Para ser sincero, eu nem uso mais isso. Estou usando o Closure Compiler do Google e isso não funciona muito bem (ou apenas complica as coisas) se você usar a configuração Avançado. Então, acabei de voltar à notação de objeto padrão.
Duncan

1
falseé o padrão para writable, enumerablee configurable. Não há necessidade de mastigar os padrões.
ceving 02/11/2015

23

Usar proxies Javascript

TLDR: adicione essa classe aos métodos utilitários e use-a em todo o código, zomba do comportamento do Enum das linguagens de programação tradicionais e, na verdade, gera erros quando você tenta acessar um enumerador que não existe ou adicionar / atualizar um enumerador. Não há necessidade de confiar 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);
  }
}

Em seguida, crie enums instanciando a classe:

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

Explicação completa:

Um recurso muito benéfico do Enums que você obtém dos idiomas tradicionais é que eles explodem (gera um erro em tempo de compilação) se você tentar acessar um enumerador que não existe.

Além de congelar a estrutura de enum simulada para impedir que valores adicionais sejam adicionados acidental / maliciosamente, nenhuma das outras respostas aborda esse recurso intrínseco do Enums.

Como você provavelmente sabe, acessar membros não existentes no JavaScript simplesmente retorna undefined e não explode seu código. Como os enumeradores são constantes predefinidas (ou seja, dias da semana), nunca deve haver um caso em que um enumerador deve ser indefinido.

Não me interpretem mal, o comportamento do JavaScript de retornar undefinedao acessar propriedades indefinidas é realmente um recurso muito poderoso da linguagem, mas não é um recurso que você deseja quando tenta zombar das estruturas tradicionais do Enum.

É aqui que os objetos Proxy brilham. Os proxies foram padronizados no idioma com a introdução do ES6 (ES2015). Aqui está a descrição do MDN:

O objeto Proxy é usado para definir o comportamento personalizado para operações fundamentais (por exemplo, pesquisa de propriedade, atribuição, enumeração, chamada de função, etc.).

Semelhante a um proxy de servidor da Web, os proxies JavaScript podem interceptar operações em objetos (com o uso de "traps", chamá-los de ganchos, se desejar) e permitem executar várias verificações, ações e / ou manipulações antes de serem concluídas (ou em alguns casos, interromper completamente as operações, exatamente o que queremos fazer se e quando tentamos fazer referência a um enumerador que não existe).

Aqui está um exemplo artificial que usa o objeto Proxy para imitar Enums. Os enumeradores neste exemplo são métodos HTTP padrão (por exemplo, "GET", "POST" etc.):

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


LADO: O que diabos é um proxy?

Lembro-me de quando comecei a ver a palavra proxy em todos os lugares, definitivamente não fazia sentido para mim por um longo tempo. Se é você agora, acho que uma maneira fácil de generalizar proxies é pensar neles como software, instituições ou até pessoas que agem como intermediários ou intermediários entre dois servidores, empresas ou pessoas.


Como fazer algo como myEnum.valueOf ("someStringValue")? Esperado: caso a string de entrada possua o valor de um elemento do enumerador, deve retornar o item. Caso nenhum item tenha esse valor de sequência, lance a exceção.
sscarduzio

@sscarduzio, você pode substituir o valueOfmétodo padrão especificando-o como método de instância na classe Enum. No entanto, por que você deseja acessá-lo dessa maneira, em vez de acessá-lo por meio de notação de ponto?
Govind Rai

Meu enum é const logLevelEnum = new Enum ({INFO: "info", DEBUG: "debug"}) e analiso a partir da entrada uma string arbitrária "info" ou "debug". Então eu preciso de algo como currentLogLevel = logLevelEnum.parseOrThrow (settings.get ( "log_level"))
sscarduzio

1
Por que você não poderia simplesmente fazer logLevelEnum[settings.get("log_level")]? adicionar parseOrThrowseria repetitivo ao que os traps de proxy já estão fazendo por você.
Govind Rai

17

Essa é uma versão antiga que eu conheço, mas a maneira como foi implementada desde a interface TypeScript é:

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

Isso permite que você procure os dois MyEnum.Barque retornam 1 e os MyEnum[1]que retornam "Bar", independentemente da ordem da declaração.


1
Além MyEnum [ "bar"] obras que retorna 1 ... <3 typescript até agora ...
David Karlas

3
e, claro, se você realmente estiver usando o Typecript:enum MyEnum { Foo, Bar, Foobar }
parliament

16

No ES7 , você pode criar um ENUM elegante com base em atributos estáticos:

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

então

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

A vantagem (de usar classe em vez de objeto literal) é ter uma classe pai, Enumentão todos os seus Enums estenderão essa classe.

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

4
Você poderia explicar por que ter uma classe dos pais é uma vantagem, por favor? Eu sinto que estou perdendo alguma coisa!
Jon G

7
Não faça isso. new ColorEnum()não faz absolutamente nenhum sentido.
Bergi

3
estendendo-se uma enumeração parece loucura, realmente
Codii

uma vez que o idioma não o suporta nativamente, faria sentido manter essa convenção e usá-la assim! concordo!
Xpto # 6/17

Acho que (?) O que o OP está obtendo é: O benefício da estática pura é que ele está disponível em todos os lugares como um singleton e você não precisa instanciar a classe - o OP não está sugerindo que você faça! Eu acho que ele está dizendo é que a superclasse Enumtem padrão estática métodos enumerador sobre ele, como getValues(), getNames(), iterate(), etc. Se for esse o caso, você não tem que reimplementar-los para cada novo tipo de enum.
Engineer

15

Esta é a solução que eu uso.

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

E você define suas enumerações assim:

var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

E é assim que você acessa suas enumerações:

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

Eu costumo usar os dois últimos métodos para mapear enumerações de objetos de mensagem.

Algumas vantagens dessa abordagem:

  • Fácil de declarar enumerações
  • Fácil de acessar suas enums
  • Suas enumerações podem ser tipos complexos
  • A classe Enum possui algum cache associativo se você estiver usando muito o getByValue

Algumas desvantagens:

  • Algum gerenciamento de memória confuso acontecendo lá, enquanto eu mantenho as referências às enumerações
  • Ainda sem segurança de tipo

14

Crie um literal de objeto:

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

12
constnão torna as propriedades do objeto imutáveis, significa apenas que a variável Modesnão pode ser reatribuída para outra coisa. Para torná-lo mais completo, use Object.freeze()ao lado const.
rvighne

Por favor não use Object.freeze. Impede que o Closure Compiler incline o objeto.
Jack Giffin

11

Se você estiver usando o Backbone , poderá obter a funcionalidade completa da enum (encontre por ID, nome, membros personalizados) gratuitamente usando o 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

suas respostas são muito complicadas

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 Concordo que sua resposta é mais eficiente e que a minha pode levar mais memória, embora você não deva assumir que todo mundo quer um enum da maneira como o C ++ a implementou. Por favor, respeite as outras respostas e os desenvolvedores que possam preferir esta sobre a sua.
Xeltor

7

Eu modifiquei a solução de 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);
    }
  }

Teste:

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

Eu vim com essa abordagem que é modelada após enumerações em Java. Estes são seguros para o tipo e, assim, você pode executarinstanceof verificações.

Você pode definir enumerações como esta:

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

Daysagora se refere ao 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"

A implementação:

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
    }

})();

Parece bom, talvez você deva verificar a existência do freezemétodo para compatibilidade com versões anteriores? Por exemplo,if (Object.freeze) { Object.freeze(values); }
FBB 23/09

Bom ponto! Vai fazer!
Vivin Paliath

6

O IE8 não suporta o método freeze ().
Fonte: http://kangax.github.io/compat-table/es5/ , clique em "Mostrar navegadores obsoletos?" na parte superior e marque IE8 e congele a interseção de linhas.

No meu projeto de jogo atual, usei abaixo, pois poucos clientes ainda usam o IE8:

var CONST_WILD_TYPES = {
    REGULAR: 'REGULAR',
    EXPANDING: 'EXPANDING',
    STICKY: 'STICKY',
    SHIFTING: 'SHIFTING'
};

Nós também poderíamos fazer:

var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

ou até isso:

var CONST_WILD_TYPES = {
    REGULAR: '1',
    EXPANDING: '2',
    STICKY: '3',
    SHIFTING: '4'
};

O último, parece mais eficiente para string, reduz sua largura de banda total se você tiver servidor e cliente trocando esses dados.
Obviamente, agora é seu dever garantir que não haja conflitos nos dados (RE, EX, etc. devem ser únicos, também 1, 2, etc. devem ser únicos). Observe que você precisa mantê-los para sempre para compatibilidade com versões anteriores.

Tarefa:

var wildType = CONST_WILD_TYPES.REGULAR;

Comparação:

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

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

Você não precisa se certificar de não atribuir números duplicados a diferentes valores de enum dessa maneira. Um novo objeto é instanciado e atribuído a todos os valores de enumeração.


Esta resposta é subestimada. É uma das minhas idéias favoritas por sua simplicidade. Na prática, acho que vou ficar com as cordas porque é mais fácil depurar por enquanto.
Domino

Hmm, apenas certifique-se de que esse código não seja chamado duas vezes ...
Andrew

4

Aqui estão algumas maneiras diferentes de implementar enumerações TypeScript .

A maneira mais fácil é apenas iterar sobre um objeto, adicionando pares de valores-chave invertidos ao objeto. A única desvantagem é que você deve definir manualmente o valor para cada membro.

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


E aqui está uma mistura de lodash para criar uma enumeração usando uma string. Embora esta versão seja um pouco mais envolvida, ela faz a numeração automaticamente para você. Todos os métodos lodash usados ​​neste exemplo têm um equivalente JavaScript regular, portanto você pode trocá-los facilmente, se desejar.

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

muito inteligente, graças
Ilan

4

Acabei de publicar um pacote NPM gen_enum permite criar rapidamente a estrutura de dados do Enum em Javascript:

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 

Uma coisa legal dessa pequena ferramenta é que, no ambiente moderno (incluindo os navegadores nodejs e IE 9+), o objeto Enum retornado é imutável.

Para mais informações, consulte https://github.com/greenlaw110/enumjs

Atualizações

Eu obsoleto o gen_enumpacote e mesclo a função no pacote constjs , que fornece mais recursos, incluindo objetos imutáveis, desserialização de string JSON, constantes de string e geração de bitmap etc. Checkout https://www.npmjs.com/package/constjs para obter mais informações

Para atualizar de gen_enumpara constjsapenas alterar a instrução

var genEnum = require('gen_enum');

para

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

4

Solução mais simples:

Crio

var Status = Object.freeze({
    "Connecting":0,
    "Ready":1,
    "Loading":2,
    "Processing": 3
});

Obter valor

console.log(Status.Ready) // 1

Obter chave

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

4

Eu criei uma classe Enum que pode buscar valores E nomes em O (1). Também pode gerar uma Matriz de Objetos contendo todos os Nomes e Valores.

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

Você pode iniciar assim:

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

Para buscar um valor (como Enums em C #):

var val2 = enum1.item2;

Para buscar um nome para um valor (pode ser ambíguo ao colocar o mesmo valor para nomes diferentes):

var name1 = enum1.GetName(0);  // "item1"

Para obter uma matriz com cada nome e valor em um objeto:

var arr = enum1.GetObjArr();

Irá gerar:

[{ Name: "item1", Value: 0}, { ... }, ... ]

Você também pode obter as opções de seleção html prontamente:

var html = enum1.GetSelectOptionsHTML();

Que contém:

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

4

Embora apenas métodos estáticos (e não propriedades estáticas) sejam suportados no ES2015 (veja aqui também, §15.2.2.2), curiosamente, você pode usar o abaixo com Babel com a es2015predefinição:

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

Eu achei que isso funcionava conforme o esperado, mesmo em módulos (por exemplo, importar o CellState enum de outro módulo) e também quando importo um módulo usando o Webpack.

A vantagem que esse método tem sobre a maioria das outras respostas é que você pode usá-lo juntamente com um verificador de tipo estático (por exemplo, Flow ) e pode afirmar, no tempo de desenvolvimento usando a verificação de tipo estático, que suas variáveis, parâmetros etc. são específicosCellState " enumeração "em vez de outra enumeração (que seria impossível distinguir se você usasse objetos ou símbolos genéricos).

atualizar

O código acima tem uma deficiência, pois permite a criação de objetos adicionais do tipo CellState(mesmo que não seja possível atribuí-los aos campos estáticos, CellStatepois estão congelados). Ainda assim, o código mais refinado abaixo oferece as seguintes vantagens:

  1. não há mais objetos do tipo CellState podem ser criados
  2. você tem a garantia de que não há duas instâncias de enumeração com o mesmo código
  3. método utilitário para recuperar o enum de uma representação de string
  4. a valuesfunção que retorna todas as instâncias da enum não precisa criar o valor de retorno da maneira acima, manual (e propensa a erros).

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

Bom exemplo :-)
Ashraf.Shk786

4

es7 way, (iterador, congelar), uso:

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'

código:

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

É assim que o Typescript o traduz enumpara Javascript:

var makeEnum = function(obj) {
    obj[ obj['Active'] = 1 ] = 'Active';
    obj[ obj['Closed'] = 2 ] = 'Closed';
    obj[ obj['Deleted'] = 3 ] = 'Deleted';
}

Agora:

makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}

No começo, fiquei confuso por que obj[1]retorna 'Active', mas depois percebi que é muito simples - o operador de atribuição atribui valor e depois o retorna:

obj['foo'] = 1
// => 1

4

Você pode fazer algo assim

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

Conforme definido nesta biblioteca. https://github.com/webmodule/foo/blob/master/foo.js#L217

Exemplo completo https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026


3

Uma maneira rápida e simples seria:

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

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

6
A função é desnecessária e fornece exatamente o mesmo resultado que o OP postou.
Sildoreth

3

No momento da redação, outubro de 2014 - aqui está uma solução contemporânea. Estou escrevendo a solução como um módulo de nó e incluímos um teste usando Mocha e Chai, além de sublinhar o JS. Você pode ignorá-los facilmente e, se preferir, use o código Enum.

Visto muitas postagens com bibliotecas excessivamente complicadas, etc. A solução para obter suporte a enum em Javascript é tão simples que realmente não é necessária. Aqui está o código:

Arquivo: 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;

E um teste para ilustrar o que ele oferece:

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

});

Como você pode ver, você obtém uma fábrica de Enum, pode obter todas as chaves simplesmente chamando enum.keys e pode combinar as próprias chaves com constantes inteiras. E você pode reutilizar a fábrica com valores diferentes e exportar os Enums gerados usando a abordagem modular do Node.

Mais uma vez, se você é apenas um usuário casual, ou no navegador, etc, apenas pegue a parte de fábrica do código, removendo potencialmente a biblioteca de sublinhado também, se você não quiser usá-la em seu código.


5
Você poderia postar uma resposta com apenas o "aqui está como fazer isso como um usuário casual que quer apenas enums, não fábricas, sublinhados ou qualquer coisa sofisticada"?
GreenAsJade

5
Mesmo que isso seja impressionante do ponto de vista dos desenvolvedores, não é muito limpo ou legível. A solução Enum do OP é mais fácil e mais legível em todos os aspectos e, portanto, melhor de usar. Ainda assim, é incrível que você tenha pensado nisso.
David David

3

É fácil de usar, eu acho. 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);

ATUALIZAR:

Existem os meus códigos auxiliares ( TypeHelper).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.