Equality Comparisons

Easy

JavaScript has three ways to compare equality: == (loose), === (strict), and Object.is(). Each behaves differently with type coercion, NaN, and signed zeros. Understanding these differences is one of the most frequently tested JavaScript interview topics.

Interactive Visualization

String Immutability

Step 1: Create
let str = "hello";
Output
"hello"
1 / 4

Key Points

  • === (strict) compares without type coercion — prefer this by default
  • == (loose) performs type coercion before comparing, following complex rules
  • Object.is() is like === but handles NaN and +0/-0 correctly
  • NaN === NaN is false — use Object.is(NaN, NaN) or Number.isNaN()
  • +0 === -0 is true — but Object.is(+0, -0) is false
  • Objects are compared by reference, not by value (even with ===)

Code Examples

Strict Equality (===)

// === compares value AND type — no coercion
console.log(5 === 5);        // true
console.log(5 === "5");      // false (different types!)
console.log(true === 1);     // false (different types!)
console.log(null === undefined); // false (different types!)

// Reference comparison for objects
const a = [1, 2, 3];
const b = [1, 2, 3];
const c = a;
console.log(a === b);  // false (different objects!)
console.log(a === c);  // true (same reference)

Strict equality checks both value and type. Objects are compared by reference, not structure.

Loose Equality (==) and Coercion

// == converts types before comparing
console.log(5 == "5");         // true (string → number)
console.log(0 == false);       // true (boolean → number)
console.log("" == false);      // true (both → 0)
console.log(null == undefined); // true (special rule!)
console.log(null == 0);        // false (null only == undefined)

// Surprising results
console.log([] == false);      // true ([] → "" → 0)
console.log("" == 0);          // true (both → 0)
console.log(" " == 0);         // true (" " → 0)

Loose equality follows complex coercion rules. The null == undefined special case is the only useful ==.

Object.is() — The Most Precise

// Object.is() is like === with two fixes:

// Fix 1: NaN is equal to itself
console.log(NaN === NaN);           // false (!)
console.log(Object.is(NaN, NaN));   // true

// Fix 2: +0 and -0 are different
console.log(+0 === -0);             // true (!)
console.log(Object.is(+0, -0));     // false

// Everything else matches ===
console.log(Object.is(5, 5));       // true
console.log(Object.is(5, "5"));     // false

// Practical: checking for NaN
const value = 0 / 0; // NaN
console.log(Number.isNaN(value)); // true (preferred)
console.log(Object.is(value, NaN)); // true

Object.is() fixes the two quirks of ===: NaN equality and signed zero comparison.

Reference vs Value Equality

// Primitives: compared by value
console.log("hello" === "hello"); // true
console.log(42 === 42);           // true

// Objects: compared by reference
const obj1 = { name: "Alice" };
const obj2 = { name: "Alice" };
console.log(obj1 === obj2);  // false (different objects!)

// Checking structural equality requires manual comparison
function deepEqual(a, b) {
  return JSON.stringify(a) === JSON.stringify(b);
}
console.log(deepEqual(obj1, obj2)); // true

Primitives compare by value, objects by reference. Deep equality requires manual comparison.

Common Mistakes

  • Using == when === is appropriate (coercion causes subtle bugs)
  • Checking NaN with === (NaN !== NaN — use Number.isNaN() instead)
  • Expecting {} === {} to be true (objects compare by reference)
  • Not knowing the null == undefined special case (it is sometimes useful)

Interview Tips

  • Always default to === and explain why (no type coercion)
  • Know the NaN and +0/-0 edge cases — these are classic interview questions
  • Explain when == is actually useful: null == undefined to check for both
  • Be ready to whiteboard the coercion steps for tricky == comparisons

Related Concepts