Docs
3.2-Comparison-Operators
3.2 Comparison Operators
Table of Contents
- •Introduction
- •Equality Operators
- •Loose Equality (==)
- •Strict Equality (===)
- •Inequality Operators
- •Relational Operators
- •String Comparison
- •Comparing Different Types
- •Object Comparison
- •Object.is() Method
- •Special Value Comparisons
- •Comparison Gotchas
- •Best Practices
Introduction
Comparison operators compare two values and return a boolean (true or false). They are fundamental for conditional logic, sorting, and data validation.
Quick Reference
| Operator | Name | Example | Result |
|---|---|---|---|
== | Loose equality | 5 == "5" | true |
=== | Strict equality | 5 === "5" | false |
!= | Loose inequality | 5 != "5" | false |
!== | Strict inequality | 5 !== "5" | true |
> | Greater than | 5 > 3 | true |
< | Less than | 5 < 3 | false |
>= | Greater than or equal | 5 >= 5 | true |
<= | Less than or equal | 5 <= 5 | true |
Equality Operators
Two Types of Equality
EQUALITY COMPARISON
Loose Equality (==) Strict Equality (===)
┌─────────────────┐ ┌──────────────────┐
│ Compares VALUE │ │ Compares VALUE │
│ with type │ │ AND TYPE │
│ coercion │ │ No coercion │
└─────────────────┘ └──────────────────┘
5 == "5" → true 5 === "5" → false
(string converts to number) (different types)
The Same vs The Same Same
// Loose equality - values are "equivalent"
console.log(5 == '5'); // true
// Strict equality - values are "identical"
console.log(5 === '5'); // false
// Same type, same value
console.log(5 === 5); // true
console.log('5' === '5'); // true
Loose Equality (==)
Type Coercion Rules
When types differ, JavaScript converts values before comparing:
TYPE COERCION ORDER (== operator)
1. If same type → compare values
2. null == undefined → true
3. Number vs String → convert String to Number
4. Boolean vs anything → convert Boolean to Number first
5. Object vs Primitive → convert Object to primitive (valueOf/toString)
Number and String
console.log(5 == '5'); // true ("5" → 5)
console.log(0 == ''); // true ("" → 0)
console.log(0 == '0'); // true ("0" → 0)
console.log(10 == '10.0'); // true ("10.0" → 10)
console.log(1 == '1.0'); // true
console.log(42 == '42abc'); // false ("42abc" → NaN)
Boolean Conversions
// Boolean converts to number FIRST, then compares
console.log(true == 1); // true (true → 1)
console.log(false == 0); // true (false → 0)
console.log(true == '1'); // true (true → 1, "1" → 1)
console.log(false == ''); // true (false → 0, "" → 0)
console.log(true == 'true'); // false (true → 1, "true" → NaN)
console.log(false == 'false'); // false (false → 0, "false" → NaN)
null and undefined
// Special rule: null == undefined is true
console.log(null == undefined); // true
console.log(undefined == null); // true
// But null and undefined don't equal anything else
console.log(null == 0); // false
console.log(null == ''); // false
console.log(null == false); // false
console.log(undefined == 0); // false
console.log(undefined == ''); // false
console.log(undefined == false); // false
Object Comparisons with ==
// Objects convert to primitives for comparison
console.log([1] == 1); // true ([1].toString() = "1" → 1)
console.log([1, 2] == '1,2'); // true ([1,2].toString() = "1,2")
console.log([] == 0); // true ([].toString() = "" → 0)
console.log([] == false); // true ([] → "" → 0, false → 0)
console.log([''] == ''); // true
console.log({} == '[object Object]'); // true
Strict Equality (===)
No Type Coercion
// Different types → always false
console.log(5 === '5'); // false
console.log(0 === false); // false
console.log(null === undefined); // false
console.log(true === 1); // false
// Same type → compare values
console.log(5 === 5); // true
console.log('hello' === 'hello'); // true
console.log(true === true); // true
Why Use Strict Equality?
// Loose equality surprises:
console.log(0 == ''); // true (unexpected!)
console.log(0 == '0'); // true
console.log('' == '0'); // false (inconsistent!)
// More surprises:
console.log(false == '0'); // true
console.log(false == []); // true
console.log([] == '0'); // false
// Strict equality is predictable:
console.log(0 === ''); // false (clear)
console.log(0 === '0'); // false (clear)
console.log('' === '0'); // false (clear)
Inequality Operators
Loose Inequality (!=)
// Returns true if values are NOT loosely equal
console.log(5 != '5'); // false (5 == "5" is true)
console.log(5 != 6); // true
console.log(null != undefined); // false (null == undefined)
console.log(0 != false); // false (0 == false)
Strict Inequality (!==)
// Returns true if values are NOT strictly equal
console.log(5 !== '5'); // true (different types)
console.log(5 !== 5); // false
console.log(null !== undefined); // true (different types)
console.log(0 !== false); // true (different types)
Relational Operators
Greater Than and Less Than
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 > 5); // false
console.log(5 < 5); // false
Greater Than or Equal, Less Than or Equal
console.log(5 >= 5); // true
console.log(5 >= 3); // true
console.log(5 >= 7); // false
console.log(5 <= 5); // true
console.log(5 <= 7); // true
console.log(5 <= 3); // false
Type Coercion in Relational Operators
// String to Number
console.log('10' > 5); // true ("10" → 10)
console.log('10' > '5'); // false (string comparison!)
// Boolean to Number
console.log(true > 0); // true (1 > 0)
console.log(false >= 0); // true (0 >= 0)
// null and undefined
console.log(null >= 0); // true (null → 0)
console.log(null > 0); // false (0 > 0)
console.log(null == 0); // false (special rule!)
console.log(undefined >= 0); // false (undefined → NaN)
console.log(undefined < 0); // false (NaN comparisons)
String Comparison
Lexicographic (Dictionary) Order
Strings are compared character by character using Unicode values:
console.log('a' < 'b'); // true
console.log('apple' < 'banana'); // true
console.log('Apple' < 'apple'); // true (uppercase < lowercase)
console.log('10' < '9'); // true (string comparison!)
console.log('10' < '2'); // true ("1" < "2" in first char)
Unicode Code Points
// Get character code
console.log('A'.charCodeAt(0)); // 65
console.log('B'.charCodeAt(0)); // 66
console.log('a'.charCodeAt(0)); // 97
console.log('b'.charCodeAt(0)); // 98
// Comparison uses these codes
console.log('A' < 'B'); // true (65 < 66)
console.log('A' < 'a'); // true (65 < 97)
console.log('Z' < 'a'); // true (90 < 97)
Case-Insensitive Comparison
let str1 = 'Apple';
let str2 = 'apple';
// Case-sensitive (default)
console.log(str1 === str2); // false
console.log(str1 < str2); // true
// Case-insensitive
console.log(str1.toLowerCase() === str2.toLowerCase()); // true
// Using localeCompare
console.log(str1.localeCompare(str2, undefined, { sensitivity: 'base' })); // 0
Sorting Strings Correctly
let words = ['banana', 'Apple', 'cherry', 'apricot'];
// Default sort (case-sensitive)
console.log(words.sort());
// ["Apple", "apricot", "banana", "cherry"]
// Case-insensitive sort
console.log(
words.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
);
// ["Apple", "apricot", "banana", "cherry"]
Comparing Different Types
Comparison Coercion Table
TYPE COERCION FOR COMPARISONS
Left Right Coercion Applied
────────────────────────────────────────
Number String String → Number
String Number String → Number
Boolean Any Boolean → Number
Object Primitive Object → Primitive
null undefined Equal (special case)
Examples
// Number vs String
console.log(10 > '5'); // true (10 > 5)
console.log(10 > '50'); // false (10 > 50)
console.log('10' > 5); // true (10 > 5)
// Boolean vs Number
console.log(true > 0); // true (1 > 0)
console.log(false < 1); // true (0 < 1)
// Boolean vs String
console.log(true > '0'); // true (1 > 0)
console.log(false == '0'); // true (0 == 0)
// With arrays
console.log([10] > 5); // true ([10] → "10" → 10)
console.log([1, 2] > 5); // false ([1,2] → "1,2" → NaN)
Object Comparison
Reference Equality
Objects are compared by reference, not by value:
let obj1 = { a: 1 };
let obj2 = { a: 1 };
let obj3 = obj1;
console.log(obj1 === obj2); // false (different references)
console.log(obj1 === obj3); // true (same reference)
console.log(obj1 == obj2); // false (still different refs)
Array Comparison
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
let arr3 = arr1;
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // true
console.log(arr1 == arr2); // false
// Compare by value - need to convert
console.log(JSON.stringify(arr1) === JSON.stringify(arr2)); // true
console.log(arr1.toString() === arr2.toString()); // true
Deep Equality
// Simple deep equality function
function deepEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return false;
}
if (obj1 === null || obj2 === null) return false;
let keys1 = Object.keys(obj1);
let keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
console.log(deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })); // true
console.log(deepEqual({ a: 1, b: 2 }, { a: 1, b: 3 })); // false
Object.is() Method
Difference from ===
Object.is() is similar to === but handles two edge cases differently:
// Case 1: NaN
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
// Case 2: Positive and Negative Zero
console.log(0 === -0); // true
console.log(Object.is(0, -0)); // false
console.log(Object.is(-0, -0)); // true
// Otherwise same as ===
console.log(Object.is(5, 5)); // true
console.log(Object.is(5, '5')); // false
console.log(Object.is({}, {})); // false
Comparison Summary
| x | y | == | === | Object.is() |
|---|---|---|---|---|
| 5 | 5 | true | true | true |
| 5 | "5" | true | false | false |
| NaN | NaN | false | false | true |
| +0 | -0 | true | true | false |
| null | undefined | true | false | false |
Special Value Comparisons
NaN Comparisons
// NaN is not equal to anything, including itself
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(NaN != NaN); // true
// Checking for NaN
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('NaN')); // false (correct behavior)
console.log(isNaN('NaN')); // true (coerces to NaN first)
null and undefined
// Only equal to each other (loose)
console.log(null == undefined); // true
console.log(null === undefined); // false
// null comparisons with relational operators
console.log(null >= 0); // true (null → 0)
console.log(null <= 0); // true (null → 0)
console.log(null == 0); // false (special rule!)
console.log(null > 0); // false
console.log(null < 0); // false
Infinity
console.log(Infinity > 100); // true
console.log(Infinity > Infinity); // false
console.log(Infinity === Infinity); // true
console.log(-Infinity < Infinity); // true
console.log(-Infinity === -Infinity); // true
Comparison Gotchas
The Transitivity Problem
// Loose equality is not transitive!
console.log('0' == 0); // true
console.log(0 == ''); // true
console.log('0' == ''); // false (should be true if transitive!)
Array Comparison Gotchas
// Empty array comparisons
console.log([] == false); // true
console.log([] == 0); // true
console.log([] == ''); // true
console.log([] == []); // false (different references!)
// Array with one element
console.log([0] == false); // true
console.log([1] == true); // true
console.log([2] == true); // false (2 !== 1)
String vs Number Comparison
// String comparison is lexicographic
console.log('100' > '99'); // false (1 < 9)
console.log('100' > '9'); // false (1 < 9)
// Number comparison
console.log(100 > 99); // true
console.log(100 > 9); // true
// Mixed - converts to number
console.log('100' > 99); // true
console.log('100' > 9); // true
The typeof null Bug
// This can cause comparison issues
let value = null;
// Wrong way to check
if (typeof value === 'object') {
// This runs for null too!
console.log("It's an object");
}
// Right way
if (value !== null && typeof value === 'object') {
console.log("It's a real object");
}
Best Practices
1. Always Use Strict Equality
// ❌ Avoid
if (x == null) {
}
if (value == 0) {
}
// ✅ Prefer
if (x === null || x === undefined) {
}
if (value === 0) {
}
// Exception: null check for both null/undefined
if (x == null) {
} // OK if you mean null OR undefined
2. Be Explicit with Type Conversions
// ❌ Implicit conversion
if (userInput == 42) {
}
// ✅ Explicit conversion
if (Number(userInput) === 42) {
}
// or
if (parseInt(userInput, 10) === 42) {
}
3. Use Proper Type Checking
// ❌ Loose type check
if (value == null) {
}
// ✅ Explicit checks
if (value === null) {
}
if (value === undefined) {
}
if (value === null || value === undefined) {
}
if (value == null) {
} // OK when intentional
4. String Comparison Best Practices
// ❌ Case-sensitive by default
if (input === 'yes') {
}
// ✅ Handle case variations
if (input.toLowerCase() === 'yes') {
}
// ✅ For locale-aware comparison
if (str1.localeCompare(str2) === 0) {
}
5. Object Comparison
// ❌ Won't work
if (arr1 === arr2) {
} // Compares references
// ✅ Compare content
if (JSON.stringify(arr1) === JSON.stringify(arr2)) {
}
// ✅ Or use a proper comparison function
if (deepEqual(obj1, obj2)) {
}
6. NaN Checking
// ❌ Won't work
if (value === NaN) {
}
// ✅ Use Number.isNaN
if (Number.isNaN(value)) {
}
// ✅ Or for older browsers
if (value !== value) {
} // NaN is the only value not equal to itself
Summary
| Operator | Name | Type Coercion | Use Case |
|---|---|---|---|
=== | Strict Equal | No | Default choice |
!== | Strict Not Equal | No | Default choice |
== | Loose Equal | Yes | Only for null check |
!= | Loose Not Equal | Yes | Rarely |
> | Greater Than | Yes | Numeric comparison |
< | Less Than | Yes | Numeric comparison |
>= | Greater/Equal | Yes | Numeric comparison |
<= | Less/Equal | Yes | Numeric comparison |
Key Rules
- •Use
===and!==by default - •
==is only useful forx == null(checks both null and undefined) - •Objects compare by reference, not value
- •String comparison is lexicographic
- •
NaN !== NaN- useNumber.isNaN() - •
Object.is()handles NaN and ±0 differently
Next Steps
Continue learning operators: