Equality checks in Javascript
In this article, you’ll find a quick refresher on equality checks in Javascript. As you may know, variable equality in JavaScript can be checked with the == operator, as well as the === operator.
To explain the differences between them, let’s look at different use cases.
Object equality
Let’s start with the easiest equality: for variables holding objects, equality happens only if the variables reference the same object.
Having the same attributes and values is simply not enough.
Let’s make sure we understand with an example:
const a = { name: "Keylor" };
const b = a;
const c = { name: "Keylor" };
a === b; // true
a === c; // false
a == b; // true
a == c; // false
On objects, our operators behave the same. They also only compare the referenced object, not the keys or the values.
Let’s try a more complex example:
drunkPerson.names === soberPerson.names; // true
drunkPerson === soberPerson; // false
drunkPerson.names == newPerson.names; // false
const names = ["Keylor"];
const drunkPerson = { names, drunk: true };
const soberPerson = { names, drunk: false };
const newPerson = { id: ["Keylor"], drunk: true };
The same idea is at play here: even though they’re attached to different arrays (which are objects), the drunkPerson.names and soberPerson.names reference the same object (the names array).
But comparing them to another array, even with the same values, will always give you false.
Let’s move to something harder!
Primitive equality
For variables containing primitive types, the two operators shine by their difference.
The === operator is often called strict equality, meaning it checks value and type equality.
The == operator is called loose or coalescing equality, meaning types don’t matter as much as the value.
The first thing to remember is that when both compared variables have the same type, both operators compare their values and return the same result.
This means that:
- Two numbers are equal only when they have the same numeric representation (3 === 3.0 and 4 + 6 == 10)
- Strings, booleans and bigints are equal when it makes intuitive sense for them to be
However, when comparing variables with different types, the strict operator will always return False, while with the coalescing operator (==), one of the variables will be converted before comparison.
The easiest is to remember that if one of the variables is a number, the other is converted to one too.
Here are a few examples:
1 == "1"; // true: Number("1") is 1
0 == false; // true: Number(false) is 0
Infinity == "+Infinity"; // true: Number("+Infinity") is Infinity
3 == "3.0"; // true: Number("3.0") is 3
Comparing values to objects
While it doesn’t make a lot of sense in most cases, when you compare an object with a primitive value, the object is first converted.
This allows us to understand the classic example of [] == “0” being false:
0 == "0"; // true: Number("0") is 0
0 == []; // true: Number([]) is 0
"0" == []; // false: String([]) is ""
After understanding all of this, you might ask yourself: is this really useful? Is loose inequality not simply a JS quirk that shouldn’t be used in real-world projects?
You’re right that in most cases, using the === operator is the better option, and it will likely save you a few headaches.
However, there are a few cases where knowing how loose equality works is helpful.
Inequality
First, loose equality can help us understand how inequality works. Indeed, when comparing two variables of different types, the rules for coalescing are the same.
We won’t dwell on the subject for too long, but here is a fun puzzle: can you find the value to choose for middle to enter the if branch?
const small = 2;
const middle = ?;
const big = "1";
if (small < middle && middle < big) {
console.log("Solved!");
}
Solution: If you guessed “+Infinity”, you were correct! Indeed, when comparing strings, the variables are ordered in the dictionary order, so “+” < “1”, but when comparing to a number, “+Infinity” is converted to Infinity.
null and undefined
Another interesting use of loose inequality is checking for nullity. Check out the following example:
const mightBeNull = getById(3);
if (!mightBeNull) {
// 0 would pass
}
if (typeof mightBeNull === "undefined") {
// null wouldn't pass, since
// typeof null is "object"
}
if (mightBeNull == null) {
// Will pass only for null and undefined
}
This is a nice trick to remember: null is always different from any other variable that isn’t null or undefined.
NaN and the total order
Finally, as a quick note, let’s look at what happens when comparing NaN (Not a Number) to another variable.
NaN > 3; // false
NaN == 3; // false
NaN < 3; // false
NaN == NaN; // false
NaN is basically the ultimate unequal value: having it on one side of a comparison operator will always return false.
This prevents the comparison order in JS from being total: NaN and 3 are simply not ordered.
Conclusion
This should give you a good overview of how equality works in JS. Of course, it’s not a reference on all possible cases, so if you want to go further, make sure to check out these links: