How can I guarantee that my enums definition doesn't change in JavaScript?

Javascript

Javascript Problem Overview


Would the following make the objects fulfil all characteristics that enums have in JavaScript? Something like:

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

// later on

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

Or is there some other way I can do this?

Javascript Solutions


Solution 1 - Javascript

Since 1.8.5 it's possible to seal and freeze the object, so define the above as:

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

or

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

and voila! JS enums.

However, this doesn't prevent you from assigning an undesired value to a variable, which is often the main goal of enums:

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

One way to ensure a stronger degree of type safety (with enums or otherwise) is to use a tool like TypeScript or Flow.

Quotes aren't needed but I kept them for consistency.

Solution 2 - Javascript

This isn't much of an answer, but I'd say that works just fine, personally

Having said that, since it doesn't matter what the values are (you've used 0, 1, 2), I'd use a meaningful string in case you ever wanted to output the current value.

Solution 3 - Javascript

UPDATE

I don't think my answer below is the best way to write enums in JavaScript anymore. See my blog post for more details: Enums in JavaScript.


Alerting the name is already possible:

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

Alternatively, you could make the values objects, so you can have the cake and eat it too:

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

In JavaScript, as it is a dynamic language, it is even possible to add enum values to the set later:

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

Remember, the fields of the enum (value, name and code in this example) are not needed for the identity check and are only there for convenience. Also the name of the size property itself does not need to be hard coded, but can also be set dynamically. So supposing you only know the name for your new enum value, you can still add it without problems:

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

Of course this means that some assumptions can no longer be made (that value represents the correct order for the size for example).

Remember, in JavaScript an object is just like a map or hash table. A set of name-value pairs. You can loop through them or otherwise manipulate them without knowing much about them in advance.

Example

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

And by the way, if you are interested in namespaces, you may want to have a look at my solution for simple but powerful namespace and dependency management for JavaScript: Packages JS

Solution 4 - Javascript

Bottom line: You can't.

You can fake it, but you won't get type safety. Typically this is done by creating a simple dictionary of string values mapped to integer values. For example:

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

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

The problem with this approach? You can accidentally redefine your enumerant, or accidentally have duplicate enumerant values. For example:

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

Edit
>What about Artur Czajka's Object.freeze? Wouldn't that work to prevent you from setting monday to thursday? – Fry Quad

Absolutely, Object.freeze would totally fix the problem I complained about. I would like to remind everyone that when I wrote the above, Object.freeze didn't really exist.

Now.... now it opens up some very interesting possibilities.

Edit 2
Here's a very good library for creating enums.

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

While it probably doesn't fit every valid use of enums, it goes a very long way.

Solution 5 - Javascript

Here's what we all want:

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

Now you can create your enums:

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

By doing this, constants can be acessed in the usual way (YesNo.YES, Color.GREEN) and they get a sequential int value (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2).

You can also add methods, by using 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 */
};


Edit - small improvement - now with varargs: (unfortunately it doesn't work properly on IE :S... should stick with previous version then)

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

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

Solution 6 - Javascript

In most modern browsers, there is a symbol primitive data type which can be used to create an enumeration. It will ensure type safety of the enum as each symbol value is guaranteed by JavaScript to be unique, i.e. Symbol() != Symbol(). For example:

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

To simplify debugging, you can add a description to enum values:

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

Plunker demo

On GitHub you can find a wrapper that simplifies the code required to initialize the 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

Solution 7 - Javascript

헦헲헹헳-헗헲혀헰헿헶헽혁헶혃헲 험혅혁헲헻혀헶헯헹헲 헩헮헿헶헮헯헹헲 헡헮헺헲혀

Let's cut straight to the problem: file size. Every other answer listed here bloats your minified code to the extreme. I present to you that for the best possible reduction in code size by minification, performance, readability of code, large scale project management, and syntax hinting in many code editors, this is the correct way to do enumerations: underscore-notation variables.


Underscore-Notation Variables

As demonstrated in the chart above and example below, here are five easy steps to get started:

  1. Determine a name for the enumeration group. Think of a noun that can describe the purpose of the enumeration or at least the entries in the enumeration. For example, a group of enumerations representing colors choosable by the user might be better named COLORCHOICES than COLORS.
  2. Decide whether enumerations in the group are mutually-exclusive or independent. If mutually-exclusive, start each enumerated variable name with ENUM_. If independent or side-by-side, use INDEX_.
  3. For each entry, create a new local variable whose name starts with ENUM_ or INDEX_, then the name of the group, then an underscore, then a unique friendly name for the property
  4. Add a ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, or INDEXLEN_ (whether LEN_ or LENGTH_ is personal preference) enumerated variable at the very end. You should use this variable wherever possible in your code to ensure that adding an extra entry to the enumeration and incrementing this value won't break your code.
  5. Give each successive enumerated variable a value one more than the last, starting at 0. There are comments on this page that say 0 should not be used as an enumerated value because 0 == null, 0 == false, 0 == "", and other JS craziness. I submit to you that, to avoid this problem and boost performance at the same time, always use === and never let == appear in your code except with typeof (e.x. typeof X == "string"). In all my years of using ===, I have never once had a problem with using 0 as an enumeration value. If you are still squeamish, then 1 could be used as the starting value in ENUM_ enumerations (but not in INDEX_ enumerations) without performance penalty in many cases.

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
}

Here is how I remember when to use INDEX_ and when to use ENUM_:

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

However, ENUM_ can, in certain circumstances, be appropriate as an index such as when counting the occurrences of each 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 that, in the code above, it's really easy to add in a new kind of pet: you would just have to append a new entry after ENUM_PET_RAT and update ENUMLEN_PET accordingly. It might be more difficult and buggy to add a new entry in other systems of enumeration.


험혅혁헲헻헱 헨헽헽헲헿헰헮혀헲 헩헮헿헶헮헯헹헲혀 헪헶혁헵 헔헱헱헶혁헶헼헻

Additionally, this syntax of enumerations allows for clear and concise class extending as seen below. To extend a class, add an incrementing number to the LEN_ entry of the parent class. Then, finish out the subclass with its own LEN_ entry so that the subclass may be extended further in the future.

Addition extension diagram

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

(Length: 2,450 bytes)

Some may say that this is less practical than other solutions: it wastes tons of space, it takes a long time to write, and it is not coated with sugar syntax. Those people would be right if they do not minify their code. However, no reasonable person would leave unminified code in the end product. For this minification, Closure Compiler is the best I have yet to find. Online access can be found here. Closure compiler is able to take all of this enumeration data and inline it, making your Javascript be super duper small and run super duper fast. Thus, Minify with Closure Compiler. Observe.


헠헶헻헶헳혆 헪헶혁헵 헖헹헼혀혂헿헲 헖헼헺헽헶헹헲헿

Closure compiler is able to perform some pretty incredible optimizations via inferences that are way beyond the capacities of any other Javascript minifier. Closure Compiler is able to inline primitive variables set to a fixed value. Closure Compiler is also able to make inferences based upon these inlined values and eliminate unused blocks in if-statements and loops.

Wringing code via 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);

(Length: 605 bytes)

Closure Compiler rewards you for coding smarter and organizing your code well because, whereas many minifiers punish organized code with a bigger minified file size, Closure Compiler is able to sift through all your cleanliness and sanity to output an even smaller file size if you use tricks like variable name enumerations. That, in this one mind, is the holy grail of coding: a tool that both assists your code with a smaller minified size and assists your mind by training better programming habits.


헦헺헮헹헹헲헿 헖헼헱헲 헦헶혇헲

Now, let us see how big the equivalent file would be without any of these enumerations.

Source Without Using Enumerations (length: 1,973 bytes (477 bytes shorter than enumerated code!))
Minified Without Using Enumerations (length: 843 bytes (238 bytes longer than enumerated code))

Chart of code sizes



As seen, without enumerations, the source code is shorter at the cost of a larger minified code. I do not know about you; but I know for sure that I do not incorporate source code into the end product. Thus, this form of enumerations is far superior insomuch that it results in smaller minified file sizes.


헖헼헼헽헲헿헮혁헶혃헲 欄 헕혂헴 헙헶혅헶헻헴

Another advantage about this form of enumeration is that it can be used to easily manage large scale projects without sacrificing minified code size. When working on a large project with lots of other people, it might be beneficial to explicitly mark and label the variable names with who created the code so that the original creator of the code can be quickly identified for collaborative bug fixing.

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


헦혂헽헲헿헶헼헿 헣헲헿헳헼헿헺헮헻헰헲 https://i.stack.imgur.com/OoOrv.png" />

Further, this form of enumeration is also much faster after minification. In normal named properties, the browser has to use hashmaps to look up where the property is on the object. Although JIT compilers intelligently cache this location on the object, there is still tremendous overhead due to special cases such as deleting a lower property from the object.

https://v8.dev/_img/elements-kinds/lattice.svg" />

But, with continuous non-sparse integer-indexed PACKED_ELEMENTS arrays, the browser is able to skip much of that overhead because the index of the value in the internal array is already specified. Yes, according to the ECMAScript standard, all properties are supposed to be treated as strings. Nevertheless, this aspect of the ECMAScript standard is very misleading about performance because all browsers have special optimizations for numeric indexes in arrays.

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

Compare the code above to the code below.

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

One might object to the code with enumerations seeming to be much longer than the code with ordinary objects, but looks can be deceiving. It is important to remember that source code size is not proportional to output size when using the epic Closure Compiler. Observe.

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

The minified code without enumerations is above and the minified code with enumerations is below.

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

The example above demonstrates that, in addition to having superior performance, the enumerated code also results in a smaller minified file size.


험헮혀혆 헗헲헯혂헴헴헶헻헴

Furthermore, this one's personal cherry on the top is using this form of enumerations along with the CodeMirror text editor in Javascript mode. CodeMirror's Javascript syntax highlighting mode highlights local variables in the current scope. That way, you know instantly when you type in a variable name correctly because if the variable name was previously declared with the var keyword, then the variable name turns a special color (cyan by default). Even if you do not use CodeMirror, then at least the browser throws a helpful [variable name] is not defined exception when executing code with mistyped enumeration names. Also, JavaScript tools such as JSLint and Closure Compiler are very loud about telling you when you mistype in an enumeration variable name. CodeMirror, the browser, and various Javascript tools put together make debugging this form of enumeration very simple and really easy.

CodeMirror highlighting demonstration

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
}

In the above snippet, you were alerted with an error because ENUM_COLORENUM_DNE does not exist.


헖헼헻헰헹혂혀헶헼헻

I think its safe to say that this methodology of enumeration is indeed the best way to go not just for minified code size, but also for performance, debugging, and collaboration.

Solution 8 - Javascript

Use Javascript Proxies

TLDR: Add this class to your utility methods and use it throughout your code, it mocks Enum behavior from traditional programming languages, and actually throws errors when you try to either access an enumerator that does not exist or add/update an enumerator. No need to rely on 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);
  }
}

Then create enums by instantiating the class:

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

Full Explanation:

One very beneficial feature of Enums that you get from traditional languages is that they blow up (throw a compile-time error) if you try to access an enumerator which does not exist.

Besides freezing the mocked enum structure to prevent additional values from accidentally/maliciously being added, none of the other answers address that intrinsic feature of Enums.

As you are probably aware, accessing non-existing members in JavaScript simply returns undefined and does not blow up your code. Since enumerators are predefined constants (i.e. days of the week), there should never be a case when an enumerator should be undefined.

Don't get me wrong, JavaScript's behavior of returning undefined when accessing undefined properties is actually a very powerful feature of language, but it's not a feature you want when you are trying to mock traditional Enum structures.

This is where Proxy objects shine. Proxies were standardized in the language with the introduction of ES6 (ES2015). Here's the description from MDN:

> The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function > invocation, etc).

Similar to a web server proxy, JavaScript proxies are able to intercept operations on objects (with the use of "traps", call them hooks if you like) and allow you to perform various checks, actions and/or manipulations before they complete (or in some cases stopping the operations altogether which is exactly what we want to do if and when we try to reference an enumerator which does not exist).

Here's a contrived example that uses the Proxy object to mimic Enums. The enumerators in this example are standard HTTP Methods (i.e. "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"


ASIDE: What the heck is a proxy?

I remember when I first started seeing the word proxy everywhere, it definitely didn't make sense to me for a long time. If that's you right now, I think an easy way to generalize proxies is to think of them as software, institutions, or even people that act as intermediaries or middlemen between two servers, companies, or people.

Solution 9 - Javascript

I've been playing around with this, as I love my enums. =)

Using Object.defineProperty I think I came up with a somewhat viable solution.

Here's a jsfiddle: http://jsfiddle.net/ZV4A6/

Using this method.. you should (in theory) be able to call and define enum values for any object, without affecting other attributes of that object.

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

Because of the attribute writable:false this should make it type safe.

So you should be able to create a custom object, then call Enum() on it. The values assigned start at 0 and increment per item.

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

Solution 10 - Javascript

This is an old one I know, but the way it has since been implemented via the TypeScript interface is:

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

This enables you to look up on both MyEnum.Bar which returns 1, and MyEnum[1] which returns "Bar" regardless of the order of declaration.

Solution 11 - Javascript

In ES7 , you can do an elegant ENUM relying on static attributes:

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

then

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

The advantage ( of using class instead of literal object) is to have a parent class Enum then all your Enums will extends that class.

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

Solution 12 - Javascript

Create an object literal:

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

Solution 13 - Javascript

This is the solution that I use.

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

And you define your enums like this:

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

And this is how you access your enums:

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

I usually use the last 2 methods for mapping enums from message objects.

Some advantages to this approach:

  • Easy to declare enums
  • Easy to access your enums
  • Your enums can be complex types
  • The Enum class has some associative caching if you are using getByValue a lot

Some disadvantages:

  • Some messy memory management going on in there, as I keep the references to the enums
  • Still no type safety

Solution 14 - Javascript

If you're using Backbone, you can get full-blown enum functionality (find by id, name, custom members) for free using 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()

Solution 15 - Javascript

your answers are far too complicated

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

Solution 16 - Javascript

I've modified the solution of 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);
    }
  }

Test:

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

Solution 17 - Javascript

I came up with this approach which is modeled after enums in Java. These are type-safe, and so you can perform instanceof checks as well.

You can define enums like this:

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

Days now refers to the Days enum:

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"

The implementation:

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
    }

})();

Solution 18 - Javascript

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

You don't need to make sure you don't assign duplicate numbers to different enum values this way. A new object gets instantiated and assigned to all enum values.

Solution 19 - Javascript

IE8 does Not support freeze() method.
Source: http://kangax.github.io/compat-table/es5/, Click on "Show obsolete browsers?" on top, and check IE8 & freeze row col intersection.

In my current game project, I have used below, since few customers still use IE8:

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

We could also do:

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

or even this:

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

The last one, seems most efficient for string, it reduces your total bandwidth if you have server & client exchanging this data.
Of course, now it's your duty to make sure there are no conflicts in the data (RE, EX, etc. must be unique, also 1, 2, etc. should be unique). Note that you need to maintain these forever for backward compatibility.

Assignment:

var wildType = CONST_WILD_TYPES.REGULAR;

Comparision:

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

Solution 20 - Javascript

I wasn't satisfied with any of the answers, so I made Yet Another Enum (YEA!).

This implementation:

  • uses more up-to-date JS
  • requires just the declaration of this one class to easily create enums
  • has mapping by name (colors.RED), string (colors["RED"]), and index (colors[0]), but you only need to pass in the strings as an array
  • binds equivalent toString() and valueOf() functions to each enum object (if this is somehow not desired, one can simply remove it - small overhead for JS though)
  • has optional global naming/storage by name string
  • freezes the enum object once created so that it can't be modified

Special thanks to Andre 'Fi''s answer for some inspiration.


The codes:

class Enums {
  static create({ name = undefined, items = [] }) {
    let newEnum = {};
    newEnum.length = items.length;
    newEnum.items = items;
    for (let itemIndex in items) {
      //Map by name.
      newEnum[items[itemIndex]] = parseInt(itemIndex, 10);
      //Map by index.
      newEnum[parseInt(itemIndex, 10)] = items[itemIndex];
    }
    newEnum.toString = Enums.enumToString.bind(newEnum);
    newEnum.valueOf = newEnum.toString;
    //Optional naming and global registration.
    if (name != undefined) {
      newEnum.name = name;
      Enums[name] = newEnum;
    }
    //Prevent modification of the enum object.
    Object.freeze(newEnum);
    return newEnum;
  }
  static enumToString() {
    return "Enum " +
      (this.name != undefined ? this.name + " " : "") +
      "[" + this.items.toString() + "]";
  }
}

Usage:

let colors = Enums.create({
  name: "COLORS",
  items: [ "RED", "GREEN", "BLUE", "PORPLE" ]
});

//Global access, if named.
Enums.COLORS;

colors.items; //Array(4) [ "RED", "GREEN", "BLUE", "PORPLE" ]
colors.length; //4

colors.RED; //0
colors.GREEN; //1
colors.BLUE; //2
colors.PORPLE; //3
colors[0]; //"RED"
colors[1]; //"GREEN"
colors[2]; //"BLUE"
colors[3]; //"PORPLE"

colors.toString(); //"Enum COLORS [RED,GREEN,BLUE,PORPLE]"

//Enum frozen, makes it a real enum.
colors.RED = 9001;
colors.RED; //0

Solution 21 - Javascript

Simplest solution:

Create

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

Get Value

console.log(Status.Ready) // 1

Get Key

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

Solution 22 - Javascript

es7 way, (iterator, freeze), usage:

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'

code:

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(', ') }

}

Solution 23 - Javascript

This can be useful:

const [CATS, DOGS, BIRDS] = ENUM();

The implementation is simple and efficient:

function * ENUM(count=1) { while(true) yield count++ }

A generator can yield the exact sequence of integers required, without knowing how many constants there are. It can also support an optional argument that specifies which (possibly negative) number to start from (defaulting to 1).

Solution 24 - Javascript

A quick and simple way would be :

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

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

Solution 25 - Javascript

Here's a couple different ways to implement TypeScript enums.

The easiest way is to just iterate over an object, adding inverted key-value pairs to the object. The only drawback is that you must manually set the value for each member.

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.Red0
// Color.Green5
// Color.Blue2
// Color[5] → Green
// Color.Blue > Color.Green → false


And here's a lodash mixin to create an enum using a string. While this version is a little bit more involved, it does the numbering automatically for you. All the lodash methods used in this example have a regular JavaScript equivalent, so you can easily switch them out if you want.

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

Solution 26 - Javascript

I've just published an NPM package gen_enum allows you to create Enum data structure in Javascript quickly:

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 

One nice thing about this little tool is in modern environment (including nodejs and IE 9+ browsers) the returned Enum object is immutable.

For more information please checkout https://github.com/greenlaw110/enumjs

Updates

I obsolete gen_enum package and merge the function into constjs package, which provides more features including immutable objects, JSON string deserialization, string constants and bitmap generation etc. Checkout https://www.npmjs.com/package/constjs for more information

To upgrade from gen_enum to constjs just change the statement

var genEnum = require('gen_enum');

to

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

Solution 27 - Javascript

I've made an Enum class that can fetch values AND names at O(1). It can also generate an Object Array containing all Names and Values.

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

You can init'd it like this:

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

To fetch a value (like Enums in C#):

var val2 = enum1.item2;

To fetch a name for a value (can be ambiguous when putting the same value for different names):

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

To get an array with each name & value in an object:

var arr = enum1.GetObjArr();

Will generate:

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

You can also get the html select options readily:

var html = enum1.GetSelectOptionsHTML();

Which holds:

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

Solution 28 - Javascript

You can do something like this

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

As defined in this library. https://github.com/webmodule/foo/blob/master/foo.js#L217

Complete example https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026

Solution 29 - Javascript

Even though only static methods (and not static properties) are supported in ES2015 (see here as well, §15.2.2.2), curiously you can use the below with Babel with the es2015 preset:

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

I found this to be working as expected even across modules (e.g. importing the CellState enum from another module) and also when I import a module using Webpack.

The advantage this method has over most other answers is that you can use it alongside a static type checker (e.g. Flow) and you can assert, at development time using static type checking, that your variables, parameters, etc. are of the specific CellState "enum" rather than some other enum (which would be impossible to distinguish if you used generic objects or symbols).

update

The above code has a deficiency in that it allows one to create additional objects of type CellState (even though one can't assign them to the static fields of CellState since it's frozen). Still, the below more refined code offers the following advantages:

  1. no more objects of type CellState may be created

  2. you are guaranteed that no two enum instances are assigned the same code

  3. utility method to get the enum back from a string representation

  4. the values function that returns all instances of the enum does not have to create the return value in the above, manual (and error-prone) way.

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

Solution 30 - Javascript

This is how Typescript translates it's enum into Javascript:

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

Now:

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

At first I was confused why obj[1] returns 'Active', but then realised that its dead simple - Assignment operator assigns value and then returns it:

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

Solution 31 - Javascript

As of writing, October 2014 - so here is a contemporary solution. Am writing the solution as a Node Module, and have included a test using Mocha and Chai, as well as underscoreJS. You can easily ignore these, and just take the Enum code if preferred.

Seen a lot of posts with overly convoluted libraries etc. The solution to getting enum support in Javascript is so simple it really isn't needed. Here is the code:

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

And a test to illustrate what it gives you:

file: enumsSpec.js

var chai = require("chai"),
    assert = chai.assert,
    expect = chai.expect,
    should = chai.should(),
    enums = require('./enums'),
    _ = require('underscore');


describe('enums', function () {

    describe('fileFormatEnum', function () {
        it('should return expected fileFormat enum declarations', function () {
            var fileFormatEnum = enums.fileFormatEnum;
            should.exist(fileFormatEnum);
            assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');
            assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');
        });
    });

    describe('encodingEnum', function () {
        it('should return expected encoding enum declarations', function () {
            var encodingEnum = enums.encodingEnum;
            should.exist(encodingEnum);
            assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format');
            assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');
        });
    });

});

As you can see, you get an Enum factory, you can get all the keys simply by calling enum.keys, and you can match the keys themselves to integer constants. And you can reuse the factory with different values, and export those generated Enums using Node's modular approach.

Once again, if you are just a casual user, or in the browser etc, just take the factory part of the code, potentially removing underscore library too if you don't wish to use it in your code.

Solution 32 - Javascript

It's easy to use, I think. 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);

UPDATE:

There is my helper codes(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"]));

Solution 33 - Javascript

I wrote enumerationjs a very tiny library to address the issue which ensures type safety, allow enum constants to inherit from a prototype, guaranties enum constants and enum types to be immutable + many little features. It allows to refactor a lot of code and move some logic inside the enum definition. Here is an example :

var CloseEventCodes = new Enumeration("closeEventCodes", {
  CLOSE_NORMAL:          { _id: 1000, info: "Connection closed normally" },
  CLOSE_GOING_AWAY:      { _id: 1001, info: "Connection closed going away" },
  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: "Connection closed due to protocol error"  },
  CLOSE_UNSUPPORTED:     { _id: 1003, info: "Connection closed due to unsupported operation" },
  CLOSE_NO_STATUS:       { _id: 1005, info: "Connection closed with no status" },
  CLOSE_ABNORMAL:        { _id: 1006, info: "Connection closed abnormally" },
  CLOSE_TOO_LARGE:       { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
    console.log(this.info); 
  }
});


CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true

Enumeration is basically a factory.

Fully documented guide available here. Hope this helps.

Solution 34 - Javascript

You can try this:

   var Enum = Object.freeze({
            Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
    		Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
            });
    
    alert(Enum.Role.Supervisor);
    alert(Enum.Color.GREEN);
    var currentColor=0;
    if(currentColor == Enum.Color.RED) {
       alert('Its Red');
    }

Solution 35 - Javascript

class Enum {
  constructor (...vals) {
    vals.forEach( val => {
      const CONSTANT = Symbol(val);
      Object.defineProperty(this, val.toUpperCase(), {
        get () {
          return CONSTANT;
        },
        set (val) {
          const enum_val = "CONSTANT";
          // generate TypeError associated with attempting to change the value of a constant
          enum_val = val;
        }
      });
    });
  }
}

Example of usage:

const COLORS = new Enum("red", "blue", "green");

Solution 36 - Javascript

Update 05.11.2020:
Modified to include static fields and methods to closer replicate "true" enum behavior.

Has anyone tried doing this with a class that contains private fields and "get" accessors? I realize private class fields are still experimental at this point but it seems to work for the purposes of creating a class with immutable fields/properties. Browser support is decent as well. The only "major" browsers that don't support it are Firefox (which I'm sure they will soon) and IE (who cares).

DISCLAIMER:
I am not a developer. I was just looking for an answer to this question and started thinking about how I sometimes create "enhanced" enums in C# by creating classes with private fields and restricted property accessors.

Sample Class

class Sizes {
    // Private Fields
    static #_SMALL = 0;
    static #_MEDIUM = 1;
    static #_LARGE = 2;

    // Accessors for "get" functions only (no "set" functions)
    static get SMALL() { return this.#_SMALL; }
    static get MEDIUM() { return this.#_MEDIUM; }
    static get LARGE() { return this.#_LARGE; }
}

You should now be able to call your enums directly.

Sizes.SMALL; // 0
Sizes.MEDIUM; // 1
Sizes.LARGE; // 2

The combination of using private fields and limited accessors means that the enum values are well protected.

Sizes.SMALL = 10 // Sizes.SMALL is still 0
Sizes._SMALL = 10 // Sizes.SMALL is still 0
Sizes.#_SMALL = 10 // Sizes.SMALL is still 0

Solution 37 - Javascript

I had done it a while ago using a mixture of __defineGetter__ and __defineSetter__ or defineProperty depending on the JS version.

Here's the enum generating function I made: https://gist.github.com/gfarrell/6716853

You'd use it like this:

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

And it would create an immutable string:int dictionary (an enum).

Solution 38 - Javascript

You just need to make an immutable object by using Object.freeze():

export const ColorEnum = Object.freeze({
    // you can only change the property values here
    // in the object declaration like in the Java enumaration
    RED: 0,
    GREEN: 1,
    BLUE: 2,
});

ColorEnum.RED = 22    // assigning here will throw an error
ColorEnum.VIOLET = 45 // even adding a new property will throw an error

Solution 39 - Javascript

Really like what @Duncan did above, but I don't like mucking up global Object function space with Enum, so I wrote the following:

function mkenum_1()
{
  var o = new Object();
  var c = -1;
  var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};

  for (i in arguments) {
    var e = arguments[i];
    if ((!!e) & (e.constructor == Object))
      for (j in e)
        f(j, (c=e[j]));
    else
      f(e, ++c);
    }

  return Object.freeze ? Object.freeze(o) : o;
}

var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');

console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);

// Output is:
// MED := 1
// LRG := 100

@Stijin also has a neat solution (referring to his blog) which includes properties on these objects. I wrote some code for that, too, which I'm including next.

function mkenum_2(seed)
{
    var p = {};

    console.log("Seed := " + seed);

    for (k in seed) {
        var v = seed[k];

        if (v instanceof Array)
            p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
        else
            p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
    }
    seed.properties = p;

    return Object.freeze ? Object.freeze(seed) : seed;
}

This version produces an additional property list allowing friendly name conversion and short codes. I like this version because one need not duplicate data entry in properties as the code does it for you.

var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });

These two can be combined into a single processing unit, mkenum, (consume enums, assign values, create and add property list). However, as I've spent far too much time on this today already, I will leave the combination as an exercise for the dear reader.

Solution 40 - Javascript

You can use Object.prototype.hasOwnProperty()

var findInEnum,
    colorEnum = {
    red : 0,
    green : 1,
    blue : 2
};

// later on

findInEnum = function (enumKey) {
  if (colorEnum.hasOwnProperty(enumKey)) {
    return enumKey+' Value: ' + colorEnum[enumKey]
  }
}

alert(findInEnum("blue"))

Solution 41 - Javascript

What is an enum in my opinion: It's an immutable object that is always accessible and you can compare items with eachother, but the items have common properties/methods, but the objects themselves or the values cannot be changed and they are instantiated only once.

Enums are imho used for comparing datatypes, settings, actions to take/reply things like that.

So for this you need objects with the same instance so you can check if it is a enum type if(something instanceof enum) Also if you get an enum object you want to be able to do stuff with it, regardless of the enum type, it should always respond in the same way.

In my case its comparing values of datatypes, but it could be anything, from modifying blocks in facing direction in a 3d game to passing values on to a specific object type registry.

Keeping in mind it is javascript and doesn't provide fixed enum type, you end up always making your own implementation and as this thread shows there are legions of implementations without one being the absoulte correct.


This is what I use for Enums. Since enums are immutable(or should be at least heh) I freeze the objects so they can't be manipulated easely.

The enums can be used by EnumField.STRING and they have their own methods that will work with their types. To test if something passed to an object you can use if(somevar instanceof EnumFieldSegment)

It may not be the most elegant solution and i'm open for improvements, but this type of immutable enum(unless you unfreeze it) is exactly the usecase I needed.

I also realise I could have overridden the prototype with a {} but my mind works better with this format ;-) shoot me.

/**
 * simple parameter object instantiator
 * @param name
 * @param value
 * @returns
 */
function p(name,value) {
	this.name = name;
	this.value = value;
	return Object.freeze(this);
}
/**
 * EnumFieldSegmentBase
 */
function EnumFieldSegmentBase() {
	this.fieldType = "STRING";
}
function dummyregex() {
}
dummyregex.prototype.test = function(str) {
	if(this.fieldType === "STRING") {
		maxlength = arguments[1];
		return str.length <= maxlength;
	}
	return true;
};

dummyregexposer = new dummyregex();
EnumFieldSegmentBase.prototype.getInputRegex = function() { 
	switch(this.fieldType) {
		case "STRING" : 	return dummyregexposer;  
		case "INT": 		return /^(\d+)?$/;
		case "DECIMAL2":	return /^\d+(\.\d{1,2}|\d+|\.)?$/;
		case "DECIMAL8": 	return /^\d+(\.\d{1,8}|\d+|\.)?$/;
		// boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
		case "BOOLEAN": 	return dummyregexposer;
	}
};
EnumFieldSegmentBase.prototype.convertToType = function($input) {
	var val = $input;
	switch(this.fieldType) {
		case "STRING" : 		val = $input;break;
		case "INT": 		val==""? val=0 :val = parseInt($input);break;
		case "DECIMAL2":	if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal2($input).toDP(2);break;
		case "DECIMAL8": 	if($input === "" || $input === null) {$input = "0"}if($input.substr(-1) === "."){$input = $input+0};val = new Decimal8($input).toDP(8);break;
		// boolean is tricky dicky. if its a boolean false, if its a string if its empty 0 or false its  false, otherwise lets see what Boolean produces
		case "BOOLEAN": 	val = (typeof $input == 'boolean' ? $input : (typeof $input === 'string' ? (($input === "false" || $input === "" || $input === "0") ? false : true) : new Boolean($input).valueOf()))  ;break;
	}
	return val;
};
EnumFieldSegmentBase.prototype.convertToString = function($input) {
	var val = $input;
	switch(this.fieldType) {
		case "STRING": 		val = $input;break;
		case "INT": 		val = $input+"";break;
		case "DECIMAL2":	val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+2 : $input.toString().indexOf('.')+2)) ;break;
		case "DECIMAL8": 	val = $input.toPrecision(($input.toString().indexOf('.') === -1 ? $input.toString().length+8 : $input.toString().indexOf('.')+8)) ;break;
		case "BOOLEAN": 	val = $input ? "true" : "false"  ;break;
	}
	return val;
};
EnumFieldSegmentBase.prototype.compareValue = function($val1,$val2) {
	var val = false;
	switch(this.fieldType) {
		case "STRING": 		val = ($val1===$val2);break;
		case "INT": 		val = ($val1===$val2);break;
		case "DECIMAL2":	val = ($val1.comparedTo($val2)===0);break;
		case "DECIMAL8": 	val = ($val1.comparedTo($val2)===0);break;
		case "BOOLEAN": 	val = ($val1===$val2);break;
	}
	return val;
};

/**
 * EnumFieldSegment is an individual segment in the 
 * EnumField
 * @param $array An array consisting of object p
 */
function EnumFieldSegment() {
	for(c=0;c<arguments.length;c++) {
		if(arguments[c] instanceof p) {
			this[arguments[c].name] = arguments[c].value;
		}
	}
	return Object.freeze(this); 
}
EnumFieldSegment.prototype = new EnumFieldSegmentBase();
EnumFieldSegment.prototype.constructor = EnumFieldSegment;


/**
 * Simple enum to show what type of variable a Field type is.
 * @param STRING
 * @param INT
 * @param DECIMAL2
 * @param DECIMAL8
 * @param BOOLEAN
 * 
 */
EnumField = Object.freeze({STRING: 		new EnumFieldSegment(new p("fieldType","STRING")), 
							INT: 		new EnumFieldSegment(new p("fieldType","INT")), 
							DECIMAL2: 	new EnumFieldSegment(new p("fieldType","DECIMAL2")), 
							DECIMAL8: 	new EnumFieldSegment(new p("fieldType","DECIMAL8")), 
							BOOLEAN:	new EnumFieldSegment(new p("fieldType","BOOLEAN"))});

Solution 42 - Javascript

The Alien solution is to make things as simple as possible:

  1. use enum keyword (reserved in javascript)

  2. If enum keyword is just reserved but not implemented in your javascript, define the following

    const enumerate = spec => spec.split(/\s*,\s*/)
      .reduce((e, n) => Object.assign(e,{[n]:n}), {}) 
    

Now, you can easily use it

const kwords = enumerate("begin,end, procedure,if")
console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)

I see no reason to make the enum values explicit variables. The scripts are morphic anyway and it makes no difference if part of your code is a string or valid code. What really matters is that you do not need to deal with tons of quotation marks whenever use or define them.

Solution 43 - Javascript

Read all the answers and didn't found any non-verbose and DRY solution. I use this one-liner:

const modes = ['DRAW', 'SCALE', 'DRAG'].reduce((o, v) => ({ ...o, [v]: v }), {});

it generates an object with human-readable values:

{
  DRAW: 'DRAW',
  SCALE: 'SCALE',
  DRAG: 'DRAG'
}

Solution 44 - Javascript

var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });

You don't need to specify an id, you can just use an empty object to compare enums.

if (incommingEnum === DaysEnum.monday) //incommingEnum is monday

EDIT: If you are going to serialize the object (to JSON for instance) you'll the id again.

Solution 45 - Javascript

You could try using https://bitbucket.org/snippets/frostbane/aAjxM.

my.namespace.ColorEnum = new Enum(
    "RED = 0",
    "GREEN",
    "BLUE"
)

It should work up to ie8.

Solution 46 - Javascript

You can use a simple funcion to invert keys and values, it will work with arrays also as it converts numerical integer strings to numbers. The code is small, simple and reusable for this and other use cases.

var objInvert = function (obj) {
    var invert = {}
    for (var i in obj) {
      if (i.match(/^\d+$/)) i = parseInt(i,10)
      invert[obj[i]] = i
    }
    return invert
}
 
var musicStyles = Object.freeze(objInvert(['ROCK', 'SURF', 'METAL','BOSSA-NOVA','POP','INDIE']))

console.log(musicStyles)

Solution 47 - Javascript

This answer is an alternative approach for specific circumstances. I needed a set of bitmask constants based on attribute sub-values (cases where an attribute value is an array or list of values). It encompasses the equivalent of several overlapping enums.

I created a class to both store and generate the bitmask values. I can then use the pseudo-constant bitmask values this way to test, for example, if green is present in an RGB value:

if (value & Ez.G) {...}

In my code I create only one instance of this class. There doesn't seem to be a clean way to do this without instantiating at least one instance of the class. Here is the class declaration and bitmask value generation code:

class Ez {
constructor() {
    let rgba = ["R", "G", "B", "A"];
    let rgbm = rgba.slice();
    rgbm.push("M");              // for feColorMatrix values attribute
    this.createValues(rgba);
    this.createValues(["H", "S", "L"]);
    this.createValues([rgba, rgbm]);
    this.createValues([attX, attY, attW, attH]);
}
createValues(a) {                // a for array
    let i, j;
    if (isA(a[0])) {             // max 2 dimensions
        let k = 1;
        for (i of a[0]) {
            for (j of a[1]) {
                this[i + j] = k;
                k *= 2;
            }
        }
    }
    else {                       // 1D array is simple loop
        for (i = 0, j = 1; i < a.length; i++, j *= 2)
            this[a[i]] = j;
   }
}

The 2D array is for the SVG feColorMatrix values attribute, which is a 4x5 matrix of RGBA by RGBAM, where M is a multiplier. The resulting Ez properties are Ez.RR, Ez.RG, etc.

Solution 48 - Javascript

export const ButtonType = Object.freeze({ 
   DEFAULT: 'default', 
   BIG: 'big', 
   SMALL: 'small'
})

source: https://medium.com/@idanlevi2/enum-in-javascript-5f2ff500f149

Solution 49 - Javascript

Here's my take on a (flagged) Enum factory. Here's a working demo.

/*
 * Notes: 
 * The proxy handler enables case insensitive property queries
 * BigInt is used to enable bitflag strings /w length > 52
*/
function EnumFactory() {
  const proxyfy = {
    construct(target, args) { 
      const caseInsensitiveHandler = { 
          get(target, key) {
          return target[key.toUpperCase()] || target[key];	
        } 
      };
      const proxified = new Proxy(new target(...args), caseInsensitiveHandler ); 
      return Object.freeze(proxified);
    },
  }
  const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);
  const throwIf = (
      assertion = false, 
      message = `Unspecified error`, 
      ErrorType = Error ) => 
      assertion && (() => { throw new ErrorType(message); })();
  const hasFlag = (val, sub) => {
    throwIf(!val || !sub, "valueIn: missing parameters", RangeError);
    const andVal = (sub & val);
    return andVal !== BigInt(0) && andVal === val;
  };

  function EnumCtor(values) {
    throwIf(values.constructor !== Array || 
    		values.length < 2 || 
        values.filter( v => v.constructor !== String ).length > 0,
      `EnumFactory: expected Array of at least 2 strings`, TypeError);
    const base = BigInt(1);
    this.NONE = BigInt(0);
    values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );
  }

  EnumCtor.prototype = {
    get keys() { return Object.keys(this).slice(1); },
    subset(sub) {
      const arrayValues = this.keys;
      return new ProxiedEnumCtor(
        [...sub.toString(2)].reverse()
          .reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )
      );
    },
    getLabel(enumValue) {
      const tryLabel = Object.entries(this).find( value => value[1] === enumValue );
      return !enumValue || !tryLabel.length ? 
        "getLabel: no value parameter or value not in enum" :
        tryLabel.shift();
    },
    hasFlag(val, sub = this) { return hasFlag(val, sub); },
  };
  
  return arr => new ProxiedEnumCtor(arr);
}

Solution 50 - Javascript

I was searching for an answer to this question too & found this page with an answer that I think seams to be different from most answers here: https://www.sohamkamani.com/javascript/enums/

I will copy over the answer part of the article to here, just in case the link gets invalid in the future or something:

> Enums with Symbols: > > Symbols let us define values that are guaranteed not to collide > with one another. > > For example:

const Summer1 = Symbol("summer")
const Summer2 = Symbol("summer")

// Even though they have the same apparent value
// Summer1 and Summer2 don't equate
console.log(Summer1 === Summer2)
// false

console.log(Summer1)

> We can define our enums using Symbols to ensure that they are not > duplicated:

const Summer = Symbol("summer")
const Autumn = Symbol("autumn")
const Winter = Symbol("winter")
const Spring = Symbol("spring")

let season = Spring

switch (season) {
    case Summer:
	console.log('the season is summer')
	break;
	case Winter:
	console.log('the season is winter')
	break;
	case Spring:
	console.log('the season is spring')
	break;
	case Autumn:
	console.log('the season is autumn')
	break;
	default:
	console.log('season not defined')
}

> Using Symbols ensures that the only way we can assign an enum value is by using the constants that we defined initially.

> Enums with Classes:

> To make our code more semantically correct, we can create a class to > hold groups of enums. > > For example, our seasons should have a way for us to identify that > they all belong to a similar classification. > > Let’s see how we can use classes and objects to create distinct enum > groups:

// Season enums can be grouped as static members of a class
class Season {
  // Create new instances of the same class as static attributes
  static Summer = new Season("summer")
  static Autumn = new Season("autumn")
  static Winter = new Season("winter")
  static Spring = new Season("spring")

  constructor(name) {
    this.name = name
  }
}

// Now we can access enums using namespaced assignments
// this makes it semantically clear that "Summer" is a "Season"
let season = Season.Summer

// We can verify whether a particular variable is a Season enum
console.log(season instanceof Season)
// true
console.log(Symbol('something') instanceof Season)
//false

// We can explicitly check the type based on each enums class
console.log(season.constructor.name)
// 'Season'

personal note: I would have used this constructor instead: (Note: sets: this.name, to a string instead of an object, looses some of the verifications below. Optionally remove the: .description. I would also like to find a way to not have to type Seasons.summer.name but instead only need: Seasons.summer to make it return a string)

  constructor(name) {
    this.name = Symbol(name).description
  }

> Listing All Possible Enum Values:

> If we used the class-based approach above, we can loop through the > keys of the Season class to obtain all the enum values under the same > group:

Object.keys(Season).forEach(season => console.log("season:", season))
// season: Summer
// season: Autumn
// season: Winter
// season: Spring

> When to Use Enums in Javascript?

> In general, enums are helpful if there are a definite number of fixed > values for any one variable_ > > For example, the crypto standard library for Node.js has a list of supported algorithms, that can be considered an enum group. > > Using enums in Javascript correctly will lead to better code that is > more stable, easier to read and less error prone.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid CitronView Question on Stackoverflow
Solution 1 - JavascriptArtur CzajkaView Answer on Stackoverflow
Solution 2 - JavascriptGarethView Answer on Stackoverflow
Solution 3 - JavascriptStijn de WittView Answer on Stackoverflow
Solution 4 - JavascriptRandolphoView Answer on Stackoverflow
Solution 5 - JavascriptAndre 'Fi'View Answer on Stackoverflow
Solution 6 - JavascriptVitalii FedorenkoView Answer on Stackoverflow
Solution 7 - JavascriptJack GView Answer on Stackoverflow
Solution 8 - JavascriptGovind RaiView Answer on Stackoverflow
Solution 9 - JavascriptDuncanView Answer on Stackoverflow
Solution 10 - JavascriptRob HardyView Answer on Stackoverflow
Solution 11 - JavascriptAbdennour TOUMIView Answer on Stackoverflow
Solution 12 - JavascripthvddView Answer on Stackoverflow
Solution 13 - JavascriptChrisView Answer on Stackoverflow
Solution 14 - JavascriptYaroslavView Answer on Stackoverflow
Solution 15 - JavascriptXeltorView Answer on Stackoverflow
Solution 16 - JavascriptDavid MiróView Answer on Stackoverflow
Solution 17 - JavascriptVivin PaliathView Answer on Stackoverflow
Solution 18 - JavascriptShivanshu GoyalView Answer on Stackoverflow
Solution 19 - JavascriptManohar Reddy PoreddyView Answer on Stackoverflow
Solution 20 - JavascriptAndrewView Answer on Stackoverflow
Solution 21 - JavascriptIlya GazmanView Answer on Stackoverflow
Solution 22 - JavascriptJoseph MerdrignacView Answer on Stackoverflow
Solution 23 - JavascriptAral RocaView Answer on Stackoverflow
Solution 24 - Javascriptuser2254487View Answer on Stackoverflow
Solution 25 - JavascriptBlake BowenView Answer on Stackoverflow
Solution 26 - JavascriptGelin LuoView Answer on Stackoverflow
Solution 27 - JavascriptOooogiView Answer on Stackoverflow
Solution 28 - JavascriptLNTView Answer on Stackoverflow
Solution 29 - JavascriptMarcus Junius BrutusView Answer on Stackoverflow
Solution 30 - JavascriptJulius DzidzevičiusView Answer on Stackoverflow
Solution 31 - JavascriptarcseldonView Answer on Stackoverflow
Solution 32 - JavascriptSherali TurdiyevView Answer on Stackoverflow
Solution 33 - JavascriptJules Sam. RandolphView Answer on Stackoverflow
Solution 34 - JavascriptMuhammad AwaisView Answer on Stackoverflow
Solution 35 - JavascriptpapiroView Answer on Stackoverflow
Solution 36 - JavascriptdsanchezView Answer on Stackoverflow
Solution 37 - JavascriptGTFView Answer on Stackoverflow
Solution 38 - JavascriptLEMUEL ADANEView Answer on Stackoverflow
Solution 39 - JavascriptAndrew PhilipsView Answer on Stackoverflow
Solution 40 - JavascriptGildas.TamboView Answer on Stackoverflow
Solution 41 - JavascriptTschallackaView Answer on Stackoverflow
Solution 42 - JavascriptLittle AlienView Answer on Stackoverflow
Solution 43 - JavascriptoluckymanView Answer on Stackoverflow
Solution 44 - JavascriptPylinuxView Answer on Stackoverflow
Solution 45 - JavascriptmikaView Answer on Stackoverflow
Solution 46 - JavascriptDavid LemonView Answer on Stackoverflow
Solution 47 - JavascriptjamessView Answer on Stackoverflow
Solution 48 - JavascriptIdanView Answer on Stackoverflow
Solution 49 - JavascriptKooiIncView Answer on Stackoverflow
Solution 50 - JavascriptSebastian NorrView Answer on Stackoverflow