JS Tip of the Day: Kinds of Comparisons

Kinds of Comparisons
Version ES2015 (Object.is), ES2016 (includes)
Level: Intermediate

There are four ways comparisons are made (equality algorithms) in JavaScript. The two most commonly known and used are loose equality (==) and strict equality (===). The other two are known as same-value equality and same-value-zero equality.

The loose equality comparison operator (==) was the first on the scene with JavaScript. It “loosely” compares two values attempting to coerce them into the same type to help make comparisons more likely to succeed. So, for example, if you compare the number 5 with the string "5" the result would be considered true with a loose comparison despite the values being two different types (number vs. string). This is great, especially for novice programmers that might want to compare user input (which is usually formatted as a string) against an expected numeric value.

console.log(5 == 5); // true
console.log("5" == "5"); // true
console.log(5 == "5"); // true

The problem with loose equality is that the rules for what happens when values aren’t the same type are complicated. Sometimes a value is coerced into a number, sometimes a string, and when that coercion happens, sometimes equality can occur unexpectedly.

console.log(0 == "0"); // true
console.log(0 == []); // true
console.log("0" == []); // false

It wasn’t long after the introduction of the loose equality operator that the strict equality operator (===) was added to the language. Strict equality will skip any coercion and compare values directly. Strings will never equal numbers and objects will never equal anything but themselves. If a conversion is needed, it must be done explicitly.

console.log(5 === 5); // true
console.log("5" === "5"); // true
console.log(5 === "5"); // false
console.log(5 === Number("5")); // true

There two odd cases where strict equality can produce some unexpected results. Both of these involve number values, one with NaN and the other with +/-0. Strict equality will always see NaN values as unequivalent and will see 0 as equal to -0.

console.log(NaN === NaN); // false
console.log(0 === -0); // true

This is corrected in a same-value comparison. A same-value comparison can be performed with Object.is(). Object.is() will make a strict comparison with two values with the exception that NaN values are equivalent and 0 is not equal to -0.

console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(0, -0)); // false

Given how similar 0 and -0 are, there are cases where you may not want them to be equivalent. This is where same-value-zero comparisons are made. These check for same-value equality except 0 and -0 are still equal.

While there is no API in JavaScript that provides you with same-value-zero comparisons directly, it is used internally in some built-ins. One example is with Array’s includes() method which will use same-value-zero when checking for values inside of an array.

let bestValues = [-0, NaN];
console.log(bestValues.includes(-0)); // true
console.log(bestValues.includes(0)); // true
console.log(bestValues.includes(NaN)); // true

Here you can see that includes() was able to find NaN in the array despite them normally not being equivalent except in same-value comparisons, but also matched 0 with -0 despite them not being equivalent in same-value comparisons.

More info: