Module 2 · Data Types & Control Flow Deep Dive ⏱ 18 min

Equality vs. Strict Equality

By the end of this lesson you will be able to:
  • Distinguish === (strict) from == (loose) and when each converts
  • Explain why objects and arrays compare by reference
  • Recognise the NaN self-comparison trap

JavaScript has two equality operators. === (strict) checks that the type and the value match, with no conversion. == (loose) converts types first, which leads to famously surprising results:

5 === "5"          // false (number vs string)
5 == "5"           // true  (loose converts, then compares)
null == undefined  // true (a special case)

Default to ===. Reach for == only when you specifically want that coercion. The reason is simple: == has a long list of conversion rules that most developers do not memorise, and every implicit conversion is a place where your code can break when data changes.

The strict operator is not just a linter preference — it is a way of making your intentions visible. When another developer sees ===, they know you meant exact equality. When they see ==, they have to stop and wonder whether the conversion was intentional or accidental.

flowchart TD
  Q["Comparing two values"] --> S["Use === (strict)"]
  Q --> L["Avoid == (loose)"]
  S --> R["No type coercion"]
  style S fill:#e0900b,color:#fff
Prefer strict === ; it never quietly converts the types it compares.

What == actually does

Loose equality follows the Abstract Equality Comparison Algorithm. You do not need to memorise it, but knowing the shape prevents nasty surprises. The short version is:

  • If the types match, == behaves like ===.
  • If the types differ, both sides are converted to numbers (with a few exceptions) and then compared.
  • null and undefined are equal to each other and nothing else.

That conversion to numbers is where the traps live:

"" == 0        // true  (empty string becomes 0)
"0" == false   // true  (both become 0)
[1, 2] == "1,2" // true  (array becomes string)

Because the rules are arbitrary-looking, most teams ban == in their style guides and rely on linters to enforce ===. The tiny amount of extra typing buys a large reduction in bug surface area.

=== is strict; == coerces types and surprises beginners.
console.log(1 === 1);
console.log(1 === "1");
console.log(null == undefined);
console.log(null === undefined);
console.log("" == 0);
console.log([1,2] == "1,2");

Reference equality: objects and arrays

Two objects (or arrays) are equal only if they are the same object in memory, not merely equivalent contents:

[1, 2] === [1, 2]   // false (two different arrays)
const a = [1, 2];
a === a             // true (same reference)

This trips up almost every beginner, and for good reason: [1, 2] and [1, 2] look identical. But each array literal creates a new object on the heap. === compares the memory address, not the contents.

The same rule applies to objects, functions, and any non-primitive value. If you need to compare contents, you must do it manually — walk the keys and values — or use a library. There is no built-in deep equality operator in JavaScript.

flowchart LR
  A1["[1, 2]"] --> M1["memory #101"]
  A2["[1, 2]"] --> M2["memory #202"]
  M1 -.->|"==="| R["false"]
  M2 -.->|"==="| R
  style M1 fill:#3776ab,color:#fff
  style M2 fill:#3776ab,color:#fff
  style R fill:#b45309,color:#fff
Two array literals create two separate objects in memory; === compares addresses.

The NaN oddity

NaN is the one value that never equals itself. NaN === NaN is false, and NaN == NaN is also false. This is a historical quirk of the IEEE 754 floating-point standard that JavaScript follows.

To test for NaN, use Number.isNaN(value). Do not use value === NaN — it will always be false.

There is one more modern tool: Object.is. It behaves like === except for two cases: Object.is(NaN, NaN) is true, and Object.is(-0, 0) is false (whereas === treats them as equal). You will rarely need Object.is in day-to-day code, but it is useful for library authors who care about edge cases.

NaN never equals itself; Number.isNaN is the safe test.
console.log(NaN === NaN);
console.log(Number.isNaN(NaN));
console.log(Number.isNaN("hello"));
console.log(Object.is(NaN, NaN));
console.log(Object.is(-0, 0));
flowchart TD
  N1["NaN"] --> Q{"=== ?"}
  N2["NaN"] --> Q
  Q -->|"false"| R["Use Number.isNaN()"]
  style Q fill:#b45309,color:#fff
  style R fill:#e0900b,color:#fff
NaN is the only value where even strict equality returns false when compared to itself.
Exercise

What does this print? === refuses to convert; == has a special case for null and undefined.

console.log(5 === "5");
console.log(null == undefined);
Exercise

Two arrays with identical contents are compared with ===. What is the result?

Exercise

Write safeEquals(a, b) that returns true if a and b are strictly equal, OR if both are NaN. For all other values use ===. So safeEquals(NaN, NaN) is true, and safeEquals(1, 1) is true, but safeEquals(1, "1") is false.

function safeEquals(a, b) {
  // return true if strictly equal or both NaN
}
Exercise

This function is meant to check whether two arrays have the same first element. It fails because it uses loose equality. Fix it by using strict equality.

function sameFirst(a, b) {
  return a[0] == b[0];
}
Exercise

Write isMissing(value) that returns true only when value is null or undefined. Do NOT use ==. Use strict equality and logical operators instead.

function isMissing(value) {
  // return true only for null or undefined, using ===
}

Recap

  • === checks type and value with no conversion; default to it everywhere.
  • == converts types before comparing and produces surprising results; avoid it unless you have a specific reason.
  • Objects and arrays compare by reference, not by contents. Two identical-looking arrays are different objects.
  • NaN === NaN is false; use Number.isNaN() to test for NaN.
  • Object.is is like === but treats NaN as equal to itself and distinguishes -0 from 0.

Next you'll learn how to declare variables safely and build strings with template literals.

Checkpoint quiz

Which operator compares type and value WITHOUT converting either side?

What is NaN === NaN?

Go deeper — technical resources