- The == operator checks if the two values are equal without caring about their data type.
- The === operator checks if the two values are equal given they have the same data type.
A great way to see this is by comparing a number and a string that represents the same number:
console.log("1" == 1) // true console.log("1" === 1) // false
- The first comparison yields true because the == operator converts the string “1” to a number and compares it with the number 1.
- The second comparison returns false because you try to compare a string with a number, which makes no sense.
As it turns out, the == operator converts the comparables to a common type before making a comparison.
A fancy word for the conversion is implicit type coercion.
In this guide, you learn:
- What is the type coercion (implicit and explicit)
- What is the difference between == and === operators
- How == and === comparison operators work with
- primitive types (strings, numbers, booleans)
- reference types (arrays and other collections)
For instance, converting a string to a number is type coercion.
You cannot coerce an object to any other type, such as to function or object.
- Implicit type coercion
- Explicit type coercion
Let’s take a moment to understand what these mean.
Explicit Type Coercion
Explicit type coercion, as the name suggests, means doing type coercion explicitly.
In layman’s terms, this means manually converting the type of a value.
Usually this means using a built-in function, such as String(), Number(), or Boolean().
For instance, let’s convert a string to a number and add another number to it
const numstr = "100" // Convert the string to number (explicit type coercion) const num = Number(numstr) console.log(num + 10)
If you did not convert the string to a number, you would not be able to sum it up with another number.
Now, let’s talk about implicit type coercion, that is, the type coercion that automatically takes place under the hood.
Implicit Type Coercion
The word implicit refers to it happening under the hood.
Implicit type coercion only takes place if you try to perform an operation on two operands of different types.
For instance, if you compare two values of a different type, implicit type coercion converts the values to Number type. Then it compares the numeric values.
Here are some examples
"1" == 1 // 1 == 1 --> true true == "true" // true == NaN --> false "0" == false // 0 == 0 --> true
Next, let’s talk about the == operator.
Loose Equality Operator (==)
If you compare two values of different data types, the == operator implicitly coerces both values to numbers.
A loose equality operator thus can return true even if the types of the compared values do not match.
"1" == 1 // true true == "true" // false "0" == false // true
But this can sometimes be problematic.
Instead of loosely comparing values, you should be able to perform a more strict comparison that ensures values are of the same type before the comparison.
This is where the strict equality operator (===) comes in.
Strict Equality Operator (===)
The strict equality operator (===) does not implicitly coerce the values being compared.
In layman’s terms, it does not convert the compared elements to the same data type.
Thus, if the compared values represent different data types, the strict comparison automatically returns false.
So a string cannot be strictly equal to a number, for example.
Here are some examples:
"1" === 1 // false true === "true" // false "0" === false // false 2 === 2 // true "A" === "A" // true
With everything you have learned so far, understanding what is the difference between == and === becomes trivial.
- The loose equality operator == uses implicit type coercion to convert the comparables to a common type if needed.
- The strict equality operator === does directly compares the values and does not convert them to a common data type.
Thus, if you try to compare values of different type, the == operator can return true, but the === operator cannot.
To this point you have seen examples with numbers, strings, and booleans.
Comparing reference types with == and === operators has a different meaning which is important to understand.
Comparing Reference Types vs Primitive Types
Examples of primitive types are numbers, strings, and booleans.
Examples of reference types are arrays, functions, and other collections.
Let’s take a look at how the comparison operators work with both primitive types and reference types in more detail.
All the primitive types define immutable values.
In other words, you cannot directly change the values of primitive types. Instead, you need to create a new primitive type.
Let’s demonstrate the immutability with a simple example:
let a = 1 a = 2
In this piece of code, you actually do not directly change the number object from 1 to 2.
Instead, you create an entirely new Number object and make the variable a reference to it.
Comparisons with Primitive Types
Now, let’s get back to the comparison operators == and ===.
As you learned, there is a difference between == and === operators when it comes to comparing primitive types:
- The == operator loosely compares the values by converting them to a common type.
- The === operator does not convert the compared objects. Instead, it checks if both the compared values are of the same type and their value is equal.
When it comes to comparing reference-type objects, the comparison operators have an entirely different meaning.
Although, the difference between the == and === operators remains.
A reference type means the objects are passed by reference in code.
Examples of reference types are arrays, functions, and other types of collections.
To demonstrate reference types, let’s create an array, copy it to a new variable, and change the original array:
let a = [1, 2, 3] let b = a a = 1000 console.log(a) // [ 1000, 2, 3 ] console.log(b) // [ 1000, 2, 3 ]
As you can see, changing the first element in the original array a also affects the “copied” array b.
This is because
let b = a does not actually create a copy of the array. Instead, it creates a new reference to the original array.
Comparisons with Reference Types
With reference types, the comparison operators (== and ===) check if two objects reference the same object.
In other words, you can use the comparison operators to check if the two objects point to the same address in memory.
Let me show you a demonstrative example.
let a = [1, 2, 3] let b = [1, 2, 3] let c = a console.log(a === b) // false console.log(a === c) // true
As you can see:
- a === b returns false. This is because a and b are different objects in memory even though they look identical.
- a === c returns true. This is because a and c point to the same object in memory.
Here is a behind-the-scenes illustration of the above:
The arrays a and c reference the same chunk of memory. This happens because of this line of code:
let c = a
As you learned, instead of copying, this piece of code creates a new reference called c that points to the original array a.
This is really important to understand, yet it is not talked about often.
For instance, this explains why you cannot check if two arrays have equal elements using the == or === operators. To do this, you need to loop through the array element by element.
The difference between the == operator and === operator in reference types is the same as in primitive types:
- The == operator converts the compared items to a common data type. If you try to compare a reference type to a primitive type, the type coercion tries to convert the reference to a primitive type.
- The === operator does not use type coercion at all. If the two compared values are both not reference types, it automatically returns false.
Now you not only understand what is the difference between == and ===, but you also know how comparison operators work differently in different contexts.
Last but not least, let’s answer the question of which comparison operator you should use.
Which Comparison Operator Should I Use: == or ===?
The == operator tries to do type coercion, which can result in strange behavior in your code.
Here are some examples of some comparisons with == that produce unexpected results:
false == 0 // true false == "" // true 0 == "" // true undefined == null // true  == false // true  == 0 // true
But when compared with === all these comparisons produce a reasonable and predictable result:
false === 0 // false false === "" // false 0 === "" // false undefined === null // false  === false // false  === 0 // false
To be on the safe side, use the === operator for making comparisons.
To understand how the == operator works, you need to understand what is implicit type coercion.
For example, to add a string to a number, the number needs to be converted to a string or the string to a number.
- The == operator converts the two comparables to a common type before comparison. This way for example a string can be equal to a number.
- The === operator does not do the type coercion. It checks if both the value and the type of the comparables are equal to one another.
Also, it is important to understand that the comparison operators == and === work entirely differently with reference types, such as arrays. Instead of comparing if the elements are equal to one another, the operators check if the two objects reference the same object in memory.
Try to use === operator as much as possible. This way you can avoid the “anomalies” related to using the == operator.
Thanks for reading.