In JavaScript, enumerations, commonly referred to as enums, serve as a way to define a set of named constants. These enums allow developers to compile a collection of identifiable values that can be utilized throughout the codebase, enhancing error prevention, boosting readability, and facilitating maintainability.
Enumerations, commonly referred to as enums, serve the purpose of representing a defined set of named constants in JavaScript. Given that JavaScript does not natively support enums, they are generally created using either objects or immutable arrays to achieve this functionality.
Weekdays, including {"SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, and SATURDAY"}, color options such as {"BLUE, RED, YELLOW, PINK, GREEN"}, seasonal classifications like {"SUMMER, AUTUMN or FALL, SPRING, WINTER"}, and geographical directions including {"EAST, WEST, NORTH, SOUTH, NORTH EAST, SOUTH EAST, and SOUTH WEST"} can all be represented within this enumeration.
Does Enum Support JavaScript?
JavaScript does not include a built-in enum data type, despite the prevalence of enum data types in various programming languages such as C++, Java, and others. However, the functionalities typically associated with enums can be replicated in JavaScript through the use of classes and objects.
Syntax:
const EnumType = {
<enumConstant1> : <value1>,
<enumConstant2> : <value2>,
...
<enumConstantN> : <valueN>
}
A prime illustration is the utilization of enum types paired with integer values within our JavaScript programming.
<!DOCTYPE html>
<html>
<head>
<title>HTML Editor</title>
</head>
<body>
<h3> Output Screen </h3>
<p> Output: </p>
<div id="output">
</div>
<div id="Error" style="color : pink">
</div>
<script>
var content = ''
var error = ''
var opDiv = document.querySelector('#output')
var opErrDiv = document.querySelector('#Error')
// actual javascript code
try {
const ourDirection = {
East: 1,
West: 2,
North: 3,
South: 4,
NorthEast: 5,
NorthWest: 6,
SouthEast: 7,
SouthWest: 8,
}
let myDirection = 1
if (myDirection === ourDirection.East) {
content += "Our direction is East" + '<br>'
}
}
catch (err) {
error += err
}
finally {
// display on output console
opDiv.innerHTML = content
opErrDiv.innerHTML = error
}
</script>
</body>
</html>
Output
This method can be utilized in smaller codebases; however, it may result in confusion under certain conditions. For instance, a developer might compose the following instruction to demonstrate that the initial letter is in lowercase, yet the string comparison could fail, yielding a false result.
<!DOCTYPE html>
<html>
<head>
<title>HTML Screen</title>
</head>
<body>
<h3> Output Screen </h3>
<p> Output: </p>
<div id="output">
</div>
<div id="Error" style="color : pink">
</div>
<script>
var content = ''
var error = ''
var opDiv = document.querySelector('#output')
var opErrDiv = document.querySelector('#Error')
// actual javascript code
try {
const Direction = {
E: 'East',
W: 'West',
N: 'North',
S: 'South',
NE: 'North-East',
NW: 'North-West',
SE: 'South-East',
SW: 'South-West',
}
let ourDirection = 'North-East'
if (ourDirection === Direction.SE) {
content += "Yes, your direction is North-East" + '<br>'
} else {
content += "No, your direction is not South-East" + '<br>'
}
}
catch (err) {
error += err
}
finally {
// display on output console
opDiv.innerHTML = content
opErrDiv.innerHTML = error
}
</script>
</body>
</html>
Output
Due to the existence of only two values within a fundamental type, programmers might inadvertently create distinct enums that conditional operators could incorrectly align. Let us explore this predicament through an example.
<!DOCTYPE html>
<html>
<head>
<title>HTML Output</title>
</head>
<body>
<h3> Output Screen </h3>
<p> Output: </p>
<div id="output">
</div>
<div id="opError" style="color : green">
</div>
<script>
var content = ''
var error = ''
var opDiv = document.querySelector('#output')
var opErrDiv = document.querySelector('#opError')
// actual javascript code
try {
const Direction = {
East: 10,
West: 20,
North: 30,
South: 40,
NorthEast: 5,
NorthWest: 6,
SouthEast: 7,
SouthWest: 8,
}
const Car = {
Audi: 10,
BMW: 20,
Tata: 30,
Kia: 40
}
if (Direction.South === Car.Kia) {
content += "Our car type is Kia" + '<br>'
}
}
catch (err) {
error += err
}
finally {
// display on output console
opDiv.innerHTML = content
opErrDiv.innerHTML = error
}
</script>
</body>
</html>
Output
In this instance, we create two separate enumeration types; however, they become mixed up when we evaluate their values within the if block. This is not the desired behavior. Additionally, employing integers or numerical values is conceptually incorrect. For instance, the different types of directions should be represented more appropriately, as they do not fall under the categories of strings, integers, or numeric values.
Enums with Symbol types
In JavaScript, the concept referred to is called Symbol. Symbols are unique and do not clash with one another. As a result, utilizing symbols for defining values proves to be more reliable and minimizes the potential for errors. The syntax is as follows.
Syntax
const EnumType = {
const EnumType = {
<enumConstant1> : Symbol(<value1>),
<enumConstant2> : Symbol(<value2>),
...
<enumConstantN> : Symbol(<valueN>)
}
Let us examine a similar scenario in which symbols are employed and ascertain whether they conflict with one another.
<!DOCTYPE html>
<html>
<head>
<title>HTML Screen</title>
</head>
<body>
<h3> Output Screen </h3>
<p> Output: </p>
<div id="output">
</div>
<div id="opError" style="color : red">
</div>
<script>
var content = ''
var error = ''
var opDiv = document.querySelector('#output')
var opErrDiv = document.querySelector('#opError')
// actual javascript code
try {
const ourDays = {
Sunday: Symbol(1),
Monday: Symbol(2),
Tuesday: Symbol(3),
Wednesday: Symbol(4),
Thursday: Symbol(5),
Friday: Symbol(6),
Saturday: Symbol(7),
}
const Car = {
Kia: Symbol(1),
Honda: Symbol(2),
Ferrari: Symbol(3),
Tata: Symbol(4),
Audi: Symbol(5),
}
if (ourDays.Tuesday === Car.Ferrari) {
content += "Our day is Thursday" + '<br>'
} else {
content += "Not checking same data types" + '<br>'
}
}
catch (err) {
error += err
}
finally {
// display on output console
opDiv.innerHTML = content
opErrDiv.innerHTML = error
}
</script>
</body>
</html>
Output
Creating Immutable Enum types
To date, there have been limited implementations of enumerations discovered. Nonetheless, several concerns arise. It is possible to modify the value of an enum type in the final strategy simply by assigning it a new value. To restrict enum updates, we can enforce immutability by passing the object to the Object.freeze method. Let’s consider this as an example.
Syntax:
const EnumType = Object.freeze({
<enumConstant1> : Symbol(<value1>),
<enumConstant2> : Symbol(<value2>),
...
<enumConstantN> : Symbol(<valueN>)
})
<!DOCTYPE html>
<html>
<head>
<title>HTML Console</title>
</head>
<body>
<h3> Output Console </h3>
<p> Output: </p>
<div id="output">
</div>
<div id="opError" style="color : red">
</div>
<script>
var content = ''
var error = ''
var opDiv = document.querySelector('#output')
var opErrDiv = document.querySelector('#opError')
// actual javascript code
try {
const ourDays = {
Monday: Symbol(1),
Tuesday: Symbol(2),
Wednesday: Symbol(3),
Thursday: Symbol(4),
Friday: Symbol(5),
Saturday: Symbol(6),
}
content += "Value of Monday: " +
JSON.stringify(ourDays.Monday.toString()) + '<br>'
ourDays.SE = Symbol(1); // updating with a new value
content += "Value of Tuesday: " +
JSON.stringify(ourDays.Tuesday.toString()) + '<br>'
// creating an immutable object type
const Car = Object.freeze({
Audi: Symbol(1),
Tata: Symbol(2),
BMW: Symbol(3),
Kia: Symbol(4),
})
content += "Value of Car: " +
JSON.stringify(Car.Audi.toString()) + '<br>'
Car.Audi = Symbol(1); // updating will not affect the old value
content += "Value of Car: " + JSON.stringify(Car.Audi.toString()) + '<br>'
}
catch (err) {
error += err
}
finally {
// display on output console
opDiv.innerHTML = content
opErrDiv.innerHTML = error
}
</script>
</body>
</html>
Output
In JavaScript, a property that can be accessed during iteration with the for…in loop or the Object.keys method is referred to as an enumerable property. By default, all properties that are established via property initialization or straightforward assignment are enumerable.
Example 1: A student object is created with attributes including registration, name, age, and marks as defined by the code. The properties of the object are traversed using a for...in loop, with each property being assigned to a variable called key. The registration, name, age, and marks corresponding to each key are output to the console using the console.log(key) statement.
// Creating an object named student
const student = {
rollno: '123',
name: 'Yshakan',
age: 20,
marks: 100
};
// prints all the keys in student object
for (const key in student) {
console.log(key);
}
Output
By default, the Enumerable attribute is set to true for all properties, as the property initializer is responsible for initializing each of them.
Example 2: The method Object.defineProperty is utilized to explicitly alter the internal enumerable attribute of a property. Moreover, we employ the function propertyIsEnumerable to assess whether a property is enumerable. If the property is indeed enumerable, it yields true; otherwise, it returns false.
const student = {
rollno: '123',
name: 'Yshakan',
age: 20,
};
Object.defineProperty(student, 'gender', {
value: 98,
configurable: true,
writable: false,
enumerable: false,
});
console.log(student.propertyIsEnumerable('rollno'));
console.log(student.propertyIsEnumerable('name'));
console.log(student.propertyIsEnumerable('age'));
console.log(student.propertyIsEnumerable('gender'));
Output
In summary
To enhance the readability of our program, we can utilize an ordered set of constants established through enumerated or enum data types. Unlike other programming languages, JavaScript does not support enumerated types natively. However, there are several methods to create enums in our JavaScript applications. The simplest form of enums can be constructed using objects that contain either integer or string values; nonetheless, this method may not be ideal in various cases, as it can lead to conflicts between different enums of the same type. One possible solution to avoid such collisions is to use symbols. The most effective approach to ensure that our enums remain immutable, thus preventing unnecessary alterations, is to implement classes as enum types.