Telerik blogs
JavaScriptT2 Dark_1200x303

This post covers similarities and differences between the abstract and strict equality operators in JavaScript, and how to use them based on the type of comparison you want to perform.

The abstract and strict comparison operators can be used to check the equality of two operands in JavaScript. Both operators will check the equality of two values and return the boolean value (true or false) based on whether the values are equal or not. This post will discuss the differences between the two operators and how to use them effectively depending on the type of comparison you want to perform.

Before we begin, let’s look at the different value data types in JavaScript and how they can be coerced to boolean values (true or false).

Coercion

Changing values from one type to another (string to number) is referred to as coercion or type conversion in JavaScript. JavaScript has eight basic value data types, which are identified as primitives or non-primitives.

Primitive Types

  • Undefined
  • Null
  • Boolean
  • String
  • Symbol
  • Number
  • BigInt

Non-Primitive Types

  • Object
  • Function
  • Array

You can be specific about your intent to convert a value from one type to another (explicit coercion) by using built-in functions such as Number(), Boolean(), etc., or you can allow JavaScript to handle the conversion for you (implicit coercion).

With this in mind, the JavaScript specification defines a list of values that, when coerced to boolean, will return false, and they include the following:

Undefined
null
false
NaN
0, -0, +0
"", " "
0n

The values listed above are known as falsy values because they evaluate to false when encountered in boolean contexts.

Let’s take an example.

let foo = ""
if (!foo) {
// this code is executed because the empty string is a falsy value
  console.log("Hello, I am a falsy value")
}

We are using a conditional statement (the if clause) to evaluate the truthiness of the value assigned to the foo variable. What happens is JavaScript implicitly coerces the value assigned to foo to a boolean, and seeing that the empty string "" is on the list of falsy values, it evaluates to false.

The following will be logged to the console:

"Hello, I am a falsy value"

If we change the value assigned to the foo variable to any of the values listed on the falsy list, they will all be coerced to the boolean value false.

Values that are not on the list of falsy values are known as truthy values. Contrary to falsy values, truthy values will be evaluated as true when encountered in boolean contexts. Considering the list of falsy values is a very short one, here are some examples of truthy values.

{}
[]
true
10
"Hello"

Comparing values in JavaScript with any of the equality operators generally results in a boolean value indicating the result of the comparison. It returns true if the two values are equal and false if they are not equal. Although the strict and loose operators are both used in JavaScript for equality comparison, the way they perform an equality check is quite different. Let us see the similarities and differences between them.

Strict Equality Operator

The strict equality operator ===, also known as triple equals, compares both the value and the type of its operands. It is a binary operator, and it uses the algorithm defined in the JavaScript specification for the IsStrictlyEqual abstract operation to compare values to check if they are equal.

It first determines whether or not the values are of the same type; if they are not, it returns false. If both values are of the same type, it checks if the values are the same; it returns false if the values do not match—except for a few cases, which we will cover in a bit.

Let’s look at some examples.

// Same type, same value
1 === 1 // true

// different types, same value
"2" === 2 // false

In this first example above, we compare the numbers 1 and 1. True is returned because they have the same value type (both numbers) and equal values. In the second example, we are comparing a numeric string literal to a number. Although they have the same value (the number 2), false is returned because they are of different types.

The strict equality operator checks if both operands are of the same type, and then it goes ahead to compare their values, but it does not perform type conversion. If they are not of the same type, it doesn’t matter what their values are. It immediately returns false, and the values are considered unequal. So the strict equality operator only returns true if both operands are equal and of the same type.

Let’s take some more examples

let a ="Ife" + "oma"
let b = "Ifeoma"

a === b // --> true
"Ify" === "ify" // false

The strict equality operator considers strings in JavaScript “equal” when the characters in the string are the same and are of the same length. In the first example above, we compare two string values with the same number of characters and length. In the second example, we are performing a case insensitive comparison. The first string Ify is capitalized, and the second string ify isn’t, so we get false.

According to JavaScript’s specification:

  1. If Type(x) is String, then:
    a. If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.

The algorithm used by the strict equality operator treats a few cases differently.

// NaN is never equal to itself
NaN === NaN // false
// zero signed zeros are equal to each other
-0 === +0 // true
0 === -0 // true
"-0" === "+0" // false

As seen in the example above, NaN is not equal to itself or any other value in JavaScript. So if both values are NaN, it returns false. Zero and signed zeros are considered equal even though they are not the same value. The strict equality operator only considers signed zeros equal if they are both numbers.

If you need to differentiate between signed zeros, you can use Object.is().

To determine if a value is NaN, use Number.isNaN().


Abstract Equality Operator

The abstract equality operator == is also known as the double equals or the loose equality operator. Using this comparison operator allows JavaScript to do type coercion when the types are different. When comparing two values with this operator, if both operands are not the same type, JavaScript attempts to resolve the data types by implicitly converting them to the same type before comparing them. The abstract equality operator uses the algorithm defined in the JavaScript specification for the IsLooselyEqual abstract operation to compare values.

According to the algorithm defined for the IsLooselyEqual abstract operation, the first thing that happens when you use the abstract equality operator for comparison is that it checks if both values are of the same type. If they are of the same type, it performs the strict equality comparison.

Under IsLooselyEqual:

  1. If Type(x) is the same as Type(y), then:
    a. Return IsStrictlyEqual(x, y).

Let’s take a look at a few examples.

let a ="Ife" + "oma"
let b = "Ifeoma"

a == b // true
15 == 15 // true

As seen in the examples above, both operands are of the same type and have the same value, so true is returned. However, when the types match, but the values are not the same, false is returned. So we can say that if both values are of the same type, the abstract equality operator and the strict equality operator do the same thing. It doesn’t do any coercion when the types match, and it simply returns false if the values are not the same.

Let’s take some examples to see what the abstract equality operator does when it encounters values whose types don’t match.

// double equals allow coercive equality between null and undefined
let a
let b = null
let c = undefined

a == b // true
b == c // true
a == c // true
a === b // false
null == 0 // false
undefined == 0 // false

// ToNumber abstract operation is called to convert the string to number
12 == "12" // true
12 == "13" // false
12 == "twelve" // false

As we can see from the example above, null and undefined compared with the abstract equality operator are coercively equal to each other and no other values in the language.

According to the algorithm defined for the IsLooselyEqual abstract operation, if a value of type string is compared to a value of type number, the ToNumber abstract operation is called on the string value to convert it to a number first before performing the comparison.

In the example above, where we’re comparing the number 12 to the string “12”, JavaScript performs the following:

12 == "12"
12 == ToNumber("12") // The string gets converted to a number
12 == 12
12 === 12 // true

After converting the string to a number, both operands are now of the same type. Since they are of the same type, JavaScript performs the strict equality comparison, and true is returned because their value is also the same.

Remember, the JavaScript specification says that it performs the strict equality comparison when comparing with the abstract equality operator and the types match. Also, after the abstract equality operator performs type coercion and the types match, it performs strict equality comparison on both values. If the values are the same, true is returned; otherwise, false is returned.

In JavaScript, the boolean values true and false are loosely equal to numbers 1 and 0 when compared with the abstract equality operator.

According to the algorithm for the IsLooselyEqual abstract operation:

  1. If Type(x) is Number and Type(y) is String, return IsLooselyEqual(x, ! ToNumber(y)).
  2. If Type(x) is String and Type(y) is Number, return IsLooselyEqual(! ToNumber(x), y).

Let’s take some examples to see how booleans behave when compared with the abstract equality operator.

// ToNumber gets called to convert the boolean to number
true == 1 // true
ToNumber(true) == 1
1 == 1
1 === 1 // true

0 == false // true
ToNumber(false) == 0
0 == 0
0 === 0 // true

The example above shows that the ToNumber() abstract operation gets called to coerce the boolean values to numbers first before comparing them. Because the types are now equal after coercion, it performs the strict equality comparison.

Comparing Objects With Abstract and Strict Equality Operators

When comparing two objects of the same type with either the abstract or strict equality operator, they are equal if both operands reference the same object.

Let’s take some examples:

let a = ["Hello", "world"]
let b = a
let c = ["Hello", "world"]

let d = { a: 1, b: 2 }
let e = d
let f = { a: 1, b: 2 }

a == b // true
a === b // true
a == c // false
b == c // false

d == e // true
d === e // true
d == f // false
e == f // false

As seen in the example above, the variables a and b both point to the same array object, so when we compared them with the abstract or strict equality operator, true was returned. The variables a and c are of the same type and even have the same value, but false was returned because they point to two objects in memory.

The variables d and e also point to the same object, so true is returned when compared. When comparing objects, the types of both operands must match, and they must reference the same object to be considered equal.

On the other hand, when you compare a primitive value with an object using the abstract equality operator, the object is first converted to its primitive equivalent before the comparison is performed.

The algorithm for the IsLooselyEqual abstract operation states that:

  1. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return IsLooselyEqual(x, ? ToPrimitive(y)).
var a = new String("Hello")
var b = "Hello"

a == b // true
a === b // false

In the example above, we created a string object by calling the string constructor with the new keyword and assigned it to the variable a. We’re comparing this string object to a string literal which we assigned to the variable b. When comparing them with the abstract equality operator, the ToPrimitive() abstract operation gets called to convert the object to a string before the comparison is performed, and that’s why true is returned. The strict equality operator sees that the comparison is between two values of different types, and it immediately returns false.

Conclusion

In JavaScript, the abstract and strict equality operators are equally useful for determining equality. We can conclude that the strict equality operator requires both the types and the values to match to be considered equal, but the abstract equality operator allows coercion when the operands are not of the same type.

We also saw that when the abstract equality operator performs type coercion, it prefers to convert to a number before proceeding with the comparison. Finally, the abstract equality operator prefers comparison between primitives. When a primitive is compared to a non-primitive, the non-primitive is first converted to its primitive equivalent.

Many books and blogs recommend using the strict equality operator as a better option; however, it depends on what you want (do you want to allow coercion or not). When writing JavaScript, you’ll most likely use coercion without thinking about it. So it doesn’t matter which one you choose—knowing the difference and understanding how they work will only make you a better developer.


Next up, you may want to explore the different data producers in JavaScript—Functions, Promises, Iterables and Observables.


Ifeoma-Imoh
About the Author

Ifeoma Imoh

Ifeoma Imoh is a software developer and technical writer who is in love with all things JavaScript. Find her on Twitter or YouTube.

Related Posts

Comments

Comments are disabled in preview mode.