with - javascript using enum




Qual é a sintaxe preferida para definir enums em JavaScript? (20)

Use Proxies JavaScript

TLDR: Adicione essa classe aos seus métodos utilitários e use-a em todo o seu código, ela simula o comportamento Enum das linguagens de programação tradicionais e, na verdade, gera erros quando você tenta acessar um enumerador que não existe.

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

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

Em seguida, crie enums instanciando a classe:

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

Explicação completa:

Uma característica muito benéfica do Enums que você obtém de linguagens tradicionais é que elas explodem (lançam um erro em tempo de compilação) se você tentar acessar um enumerador que não existe.

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

Como você provavelmente está ciente, o acesso a membros não existentes em 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 deva ser indefinido.

Não me entenda mal, o comportamento do JavaScript de retornar undefined ao acessar propriedades indefinidas é, na verdade, um recurso muito poderoso da linguagem, mas não é um recurso que você deseja quando está tentando imitar as estruturas tradicionais do Enum.

É aqui que os objetos Proxy brilham. 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, invocação de função, etc.).

Semelhante a um proxy de servidor web, proxies JavaScript são capazes de interceptar operações em objetos (com o uso de "armadilhas", chamá-los de ganchos se você quiser) e permitir que você execute várias verificações, ações e / ou manipulações antes de concluírem (ou em alguns casos, parando completamente as operações, o que é 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 (ou seja, "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(enumObj) {
  // The Enum class instantiates a JavaScript Proxy object.
  // Instantiating a `Proxy` object requires two parameters, 
  // a `target` object and a `handler`. We first define the handler,
  // then use the handler to instantiate a Proxy.

  // A proxy handler is simply an object whose properties
  // are functions which define the behavior of the proxy 
  // when an operation is performed on it. For enums, we 
  // need to define behavior that lets us check what enumerator
  // is being accessed. This can be done by defining the "get" trap.
  
  const handler = {
    get: function(target, name) {
      if (target[name]) {
        return target[name]
      }
      throw new Error(`No such enumerator: ${name}`)
    }
  }
  
  
  // Freeze the target object to prevent modifications
  return new Proxy(Object.freeze(enumObj), handler)
}


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

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

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

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

ASIDE: O que diabos é um proxy?

Eu me lembro 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 mesmo pessoas que atuam como intermediários ou intermediários entre dois servidores, empresas ou pessoas.

Qual é a sintaxe preferida para definir enums em JavaScript? Algo como:

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

// later on

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

Ou há um idioma mais preferível?


A "Sintaxe Perferida" pela maioria das pessoas já foi listada acima. No entanto, existe um grande problema: desempenho. Nem uma única das respostas acima tem um desempenho muito bom e todas elas incham seu tamanho de código ao extremo. Para desempenho real, facilidade de leitura de código e uma redução sem precedentes no tamanho do código por minificação, essa é a maneira correta de fazer enumerações.

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

// later on

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

Além disso, essa sintaxe permite a ampliação clara e concisa da classe, como visto abaixo.

(Comprimento: 2.450 bytes)

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

    const ENUM_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          ENUMLEN_PIXELCOLOR   = 1,
          ENUM_SOLIDCOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_SOLIDCOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_SOLIDCOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUMLEN_SOLIDCOLOR   = ENUMLEN_PIXELCOLOR+3,
          ENUM_ALPHACOLOR_R    = ENUMLEN_PIXELCOLOR+0,
          ENUM_ALPHACOLOR_G    = ENUMLEN_PIXELCOLOR+1,
          ENUM_ALPHACOLOR_B    = ENUMLEN_PIXELCOLOR+2,
          ENUM_ALPHACOLOR_A    = ENUMLEN_PIXELCOLOR+3,
          ENUMLEN_ALPHACOLOR   = ENUMLEN_PIXELCOLOR+4,
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(rawstr) {
        rawstr = rawstr.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[ENUM_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[ENUM_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[ENUM_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[ENUM_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[ENUM_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

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

Alguns podem dizer que isso é menos prático do que outras soluções: ele libera toneladas de espaço, leva muito tempo para escrever, e não é revestido com a sintaxe do açúcar. Sim, essas pessoas teriam razão se não minificassem seu código. No entanto, nenhuma pessoa razoável deixaria código não minado no produto final. Para esta minificação, o Closure Compiler é o melhor que eu ainda tenho para encontrar. Acesso on-line pode ser encontrado here . O compilador de fechamento é capaz de levar todos estes dados de enumeração e inline isto, fazendo seu javascript correr super duper rápido e ser super duper pequeno. Observar.

(Comprimento: 605 bytes)

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

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

Fonte sem essas enumerações (duração: 1.973 bytes (477 bytes menor!))
Minificado sem essas enumerações (comprimento: 843 bytes (238 bytes mais ))

Como visto, sem enumerações, o código-fonte é menor ao custo de um código minificado maior. Eu não sei sobre você, eu sei com certeza que eu não gosto de incorporar o código-fonte no produto final, tornando este modo de enumeração muito superior a ponto de resultar em tamanhos menores de arquivos. Acrescente a isso que essa forma de enumeração é muito mais rápida. De fato, essa forma de enumeração é o caminho a percorrer.


Crie um literal de objeto:

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

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

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

ou

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

e voila! Enums JS.

No entanto, isso não impede que você atribua um valor indesejado a uma variável, que geralmente é o objetivo principal de enums:

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

Uma maneira de garantir um grau mais forte de segurança de tipos (com enums ou outros) é usar uma ferramenta como o TypeScript ou o Flow .

Source

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


Este é um antigo que eu conheço, mas o modo como ele foi implementado pela interface do 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ê MyEnum.Bar tanto MyEnum.Bar que retorna 1, quanto MyEnum[1] que retorna "Bar", independentemente da ordem de declaração.


Eu modifiquei a solução do 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

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

Dito isto, uma vez que não importa quais são os valores (você usou 0, 1, 2), eu usaria uma string significativa no caso de você querer produzir o valor atual.


Na maioria dos navegadores modernos, existe um tipo de dados primitivo de symbol que pode ser usado para criar uma enumeração. Isso garantirá a segurança de tipo do enum, já que cada valor de símbolo é garantido pelo JavaScript para ser exclusivo, 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 enum:

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

Demonstração de Plunker

No GitHub você pode encontrar um wrapper que simplifica o código necessário para inicializar o enum:

const color = new Enum("RED", "BLUE")

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE

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 verifique o IE8 e congele a interseção da coluna.

No meu atual projeto de jogo, usei abaixo, já que 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.
Claro, 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). Note 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
}

Resumindo: Você não pode.

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

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

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

O problema com essa abordagem? Você pode redefinir acidentalmente seu enumerante ou, acidentalmente, ter valores de enumerador duplicados. Por exemplo:

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

Editar

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

Absolutamente, Object.freeze resolveria totalmente o problema que eu reclamei. Eu gostaria de lembrar a todos que quando eu escrevi acima, Object.freeze não existia realmente.

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

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

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

Apesar de provavelmente não se adequar a todos os usos válidos de enums, ele é muito longo.


suas respostas são complicadas demais

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

ATUALIZAÇÃO : Obrigado por todos os votos positivos para todos, mas eu não acho que a minha resposta abaixo é a melhor maneira de escrever enums no Javascript mais. Veja minha postagem no blog para 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);
   }
}

Alternativamente, você poderia fazer os objetos valores, assim você pode ter 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);
}

Em Javascript, como é uma linguagem dinâmica, é possível adicionar valores enum ao conjunto depois:

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

Lembre-se de que os campos do enum (valor, nome e código neste exemplo) não são necessários para a verificação de identidade e estão lá apenas por conveniência. Além disso, o próprio nome da propriedade de tamanho não precisa ser codificado, mas também pode ser definido dinamicamente. Portanto, supondo que você saiba apenas o nome do novo valor de enum, ainda é possível adicioná-lo sem problemas:

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

Claro que 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, em JavaScript, um objeto é como um mapa ou hashtable. Um conjunto de pares de nome e valor. Você pode percorrê-los ou manipulá-los sem saber muito sobre eles com antecedência.

POR 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 namespaces, você pode querer dar uma olhada na minha solução para um simples mas poderoso namespace e gerenciamento de dependências para javascript: Packages JS


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"

Você pode fazer algo assim

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

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

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


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

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

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


É fácil de usar, eu acho. https://.com/a/32245370/4365315

var A = {a:11, b:22}, 
enumA = new TypeHelper(A);

if(enumA.Value === A.b || enumA.Key === "a"){ 
... 
}

var keys = enumA.getAsList();//[object, object]

//set
enumA.setType(22, false);//setType(val, isKey)

enumA.setType("a", true);

enumA.setTypeByIndex(1);

ATUALIZAR:

Existem meus códigos auxiliares ( TypeHelper).

var Helper = {
    isEmpty: function (obj) {
        return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
    },

    isObject: function (obj) {
        return (typeof obj === 'object');
    },

    sortObjectKeys: function (object) {
        return Object.keys(object)
            .sort(function (a, b) {
                c = a - b;
                return c
            });
    },
    containsItem: function (arr, item) {
        if (arr && Array.isArray(arr)) {
            return arr.indexOf(item) > -1;
        } else {
            return arr === item;
        }
    },

    pushArray: function (arr1, arr2) {
        if (arr1 && arr2 && Array.isArray(arr1)) {
            arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
        }
    }
};
function TypeHelper() {
    var _types = arguments[0],
        _defTypeIndex = 0,
        _currentType,
        _value,
        _allKeys = Helper.sortObjectKeys(_types);

    if (arguments.length == 2) {
        _defTypeIndex = arguments[1];
    }

    Object.defineProperties(this, {
        Key: {
            get: function () {
                return _currentType;
            },
            set: function (val) {
                _currentType.setType(val, true);
            },
            enumerable: true
        },
        Value: {
            get: function () {
                return _types[_currentType];
            },
            set: function (val) {
                _value.setType(val, false);
            },
            enumerable: true
        }
    });
    this.getAsList = function (keys) {
        var list = [];
        _allKeys.forEach(function (key, idx, array) {
            if (key && _types[key]) {

                if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                    var json = {};
                    json.Key = key;
                    json.Value = _types[key];
                    Helper.pushArray(list, json);
                }
            }
        });
        return list;
    };

    this.setType = function (value, isKey) {
        if (!Helper.isEmpty(value)) {
            Object.keys(_types).forEach(function (key, idx, array) {
                if (Helper.isObject(value)) {
                    if (value && value.Key == key) {
                        _currentType = key;
                    }
                } else if (isKey) {
                    if (value && value.toString() == key.toString()) {
                        _currentType = key;
                    }
                } else if (value && value.toString() == _types[key]) {
                    _currentType = key;
                }
            });
        } else {
            this.setDefaultType();
        }
        return isKey ? _types[_currentType] : _currentType;
    };

    this.setTypeByIndex = function (index) {
        for (var i = 0; i < _allKeys.length; i++) {
            if (index === i) {
                _currentType = _allKeys[index];
                break;
            }
        }
    };

    this.setDefaultType = function () {
        this.setTypeByIndex(_defTypeIndex);
    };

    this.setDefaultType();
}

var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var enumA = new TypeHelper(TypeA, 4);

document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setType("200L", false);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

enumA.setDefaultType();
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setTypeByIndex(1);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

document.writeln("is equals = ", (enumA.Value == TypeA["2"]));


Acabei de publicar um pacote NPM gen_enum permite criar rapidamente a estrutura de dados 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 sobre essa pequena ferramenta é no ambiente moderno (incluindo os navegadores nodejs e IE 9+) que o objeto Enum retornado é imutável.

Para mais informações por favor checkout https://github.com/greenlaw110/enumjs

Atualizações

Eu obsoleto gen_enumpacote e mesclar a função em constjs pacote, que fornece mais recursos, incluindo objetos imutáveis, deserialização de strings JSON, constantes de seqüência de caracteres e geração de bitmap etc. Checkout constjs para mais informações

Para atualizar apenas gen_enumpara constjsalterar a instrução

var genEnum = require('gen_enum');

para

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

Até a data de outubro de 2014 - então aqui está uma solução contemporânea. Estou escrevendo a solução como um módulo de nó e incluí um teste usando Mocha e Chai, bem como underscoreJS. Você pode facilmente ignorá-los e apenas pegar o código Enum, se preferir.

Visto um monte de posts 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 isso te dá:

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 Enum, você pode obter todas as chaves simplesmente chamando enum.keys, e você 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 Nó.

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


Eu criei this abordagem que é modelada após enums em Java. Eles são seguros para tipos e, portanto, você também pode executar instanceofverificações.

Você pode definir enums como este:

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
    }

})();

Eu tinha feito há algum tempo atrás usando uma mistura de __defineGetter__e __defineSetter__ou definePropertydependendo da versão JS.

Aqui está a função geradora de enum que eu fiz: https://gist.github.com/gfarrell/6716853

Você usaria assim:

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

E criaria uma string imutável: int dictionary (um enum).


Mesmo que apenas métodos estáticos (e não propriedades estáticas) sejam suportados no ES2015 (veja here também, §15.2.2.2), curiosamente você pode usar o abaixo com o 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);

Descobri que isso está funcionando conforme o esperado, mesmo em todos os módulos (por exemplo, importando o CellStateenum de outro módulo) e também quando eu importo um módulo usando o Webpack.

A vantagem deste método tem sobre a maioria das outras respostas é que você pode usá-lo ao lado de um verificador de tipo estático (por exemplo Flow ) e você pode afirmar, no tempo de desenvolvimento usando a verificação de tipo estático, que suas variáveis, parâmetros, etc. são da específico CellState" enum "em vez de algum outro enum (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 criar objetos adicionais do tipo CellState(mesmo que não seja possível atribuí-los aos campos estáticos, CellStatepois está congelado). Ainda assim, o código abaixo mais refinado oferece as seguintes vantagens:

  1. não mais objetos do tipo CellStatepodem ser criados
  2. você tem a garantia de que não há duas instâncias de enum atribuídas ao 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 do enum não precisa criar o valor de retorno no modo acima, manual (e propenso 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;




enums