cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Equality & sameness

Compare JavaScript values with confidence by learning ==, ===, Object.is, and SameValueZero step by step.

By the end you can
  • 01
    Choose the right judgeKnow when ===, ==, Object.is, includes, Map, and Set use different sameness rules.
  • 02
    Trace loose equalityWalk the spec-shaped == algorithm instead of memorizing a giant table.
  • 03
    Handle NaN and -0Recognize the two numeric edge cases that explain most surprising equality results.

Four sameness judges

JavaScript does not have one idea called equality. It has several sameness algorithms, each built for a different job. Earlier comparison lessons introduced ===, ==, NaN, and the null/undefined quirk. This lesson formalizes the rulebooks so you can predict them instead of memorizing a giant matrix.

Real-life analogyFour judges, four rulebooks

Imagine four judges deciding whether two badges belong to the same person. One refuses translations, one allows them, one uses a microscope, and one checks a guest list. Give them the same pair of values and their verdicts can differ because their jobs differ.

In real life: A strict judge checks the ID exactly
In JavaScript: === requires the same type and same value
In real life: A translator judge lets people convert papers first
In JavaScript: == converts, then compares
In real life: A microscope judge notices tiny details
In JavaScript: Object.is notices NaN and the sign of zero
In real life: A guest-list judge is practical
In JavaScript: SameValueZero powers includes, Map, and Set

Where the analogy stops: The judges are not people choosing opinions. They are deterministic algorithms in the language specification. The analogy helps you remember which details each one cares about.

The names you will see are IsStrictlyEqual for ===, IsLooselyEqual for ==, SameValue for Object.is, and SameValueZero for includes, Map, and Set. SameValueZero is SameValue except that it treats -0 and +0 as the same.

The four sameness algorithms at a glance
AlgorithmWhere you meet itCoerces?NaN equals NaN?-0 equals +0?
Strict equality=== and !==NoNoYes
Loose equality== and !=YesNoYes
SameValueObject.isNoYesNo
SameValueZeroincludes, Map, SetNoYesYes

Strict equality: same type, same value

INTERACTIVE

Strict equality is your default judge. It does not convert either side. If the types differ, the answer is immediately false. For primitives of the same type, it compares the value. For objects, arrays, and functions, the value is the reference: whether both sides point at the same object.

Strict equality edge casesPop out in the code editor (opens in a new tab)JavaScript
const shared = { id: 1 };const alsoShared = shared;const twin = { id: 1 }; console.log(1 === "1");console.log(shared === alsoShared);console.log(shared === twin);console.log(NaN === NaN);console.log(-0 === +0);

The two historical exceptions are the ones professionals still trip over: NaN === NaN is false, and -0 === +0 is true. Use the playground to ask all four judges about those cases, plus strings, numbers, BigInts, arrays, and object references.

Four judges comparison playground
The four checksPop out in the code editor (opens in a new tab)JavaScript
const a = NaN;const b = NaN;console.log(a === b);console.log(a == b);console.log(Object.is(a, b));console.log([a].includes(b));
NaNvs NaN
===false

Strict equality has the historical rule that NaN is not equal to itself.

==false

Same type, so == delegates to strict equality.

Object.istrue

SameValue says NaN is the same NaN value.

SameValueZerotrue

SameValueZero matches NaN with NaN.

Try it yourself

You selected NaN vs NaN. The verdicts below are computed with real JavaScript operators and collection membership.

Pick a pair and compare the real result from strict equality, loose equality, SameValue, and SameValueZero.
Default advice

Use === unless you can name the conversion you want. It keeps the question local: type and value match, or they do not.

The loose equality algorithm

STEP THROUGH

Loose equality is not random. It is an algorithm that repeatedly asks, “Do these types already match? If not, is there a conversion rule for this pair?” The previous Type coercion lesson introduced ToPrimitive, ToNumber, and ToBoolean; == is one place those operations become visible.

Real-life analogyLoose equality compares prices after conversion

To compare two prices, first put both in rupees. Loose equality does the same kind of work: it may convert values before comparing them.

In real life: Prices already in rupees
In JavaScript: Same type: use strict equality
In real life: Two missing price labels
In JavaScript: null == undefined
In real life: Convert a price to rupees first
In JavaScript: String, boolean, or object conversion
In real life: No price can be compared
In JavaScript: No matching rule means false

Where the analogy stops: Real prices have currencies and exchange rates. JavaScript follows fixed conversion rules, then makes the final strict comparison.

The useful skeleton is:

  1. Same type? Use strict equality.
  2. null and undefined? They equal each other.
  3. Number and string? Convert the string with ToNumber.
  4. Boolean? Convert it to 1 or 0 first.
  5. Object and primitive? Convert the object with ToPrimitive.
  6. BigInt with string or number? Use the special safe rules.
  7. No rule? The result is false.
Step through ==
Step 0 of 4Ready
Your turn: follow the blue line

Walk the loose-equality rulebook. The replay mirrors the spec branches for the chosen pair and checks the final result against real JavaScript ==.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
// 1. If types match, use strict equality.// 2. null and undefined equal only each other.// 3. Strings, booleans, and objects may convert.// 4. BigInt has careful string/number rules.// 5. If no rule matches, return false.
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Choose a pair for the loose-equality algorithm

Changing the pair starts the algorithm from the top.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
The one common == pattern

value == null is a deliberate shorthand for value === null || value === undefined. It does not match false, 0, or an empty string.

Object.is, NaN & -0

STEP THROUGH

Object.is(a, b) exposes the SameValue algorithm. It does not coerce, object references still matter, and it differs from === in exactly two numeric places: Object.is(NaN, NaN) is true, while Object.is(-0, +0) is false.

NaN and -0 under Object.is
Step 0 of 7Ready
Your turn: follow the blue line

Step through the two values strict equality handles oddly: NaN and negative zero.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
console.log(rounded);console.log(1 / rounded);console.log(Object.is(rounded, -0));console.log(JSON.parse(JSON.stringify(rounded)));console.log(Number.isNaN(NaN));console.log(NaN !== NaN);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Negative zero can appear from calculations such as Math.round(-0.4) or -1 * 0. Most UI text does not care, but numeric code sometimes does because 1 / -0 gives -Infinity. A JSON round trip erases the sign: JSON.stringify(-0) becomes "0".

Checking for NaN

Prefer Number.isNaN(value). The old trick value !== value also detects NaN, because no other JavaScript value is unequal to itself, but it reads like a riddle.

SameValueZero in includes & Map

INTERACTIVE

SameValueZero is the practical collection judge. It is like Object.is for NaN, but like === for signed zero. That is why [NaN].includes(NaN) is true, [NaN].indexOf(NaN) is -1, and new Set([-0]).has(0) is true.

SameValueZero in real methods
Step 0 of 6Ready
Your turn: follow the blue line

See why includes, Map, and Set feel friendlier than indexOf around NaN and zero signs.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
console.log([NaN].indexOf(NaN));const map = new Map([[NaN, "stored"]]);console.log(map.get(NaN));const set = new Set([-0]);console.log(set.has(0));
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
includes, Map, and Set verdicts
SameValueZero checksPop out in the code editor (opens in a new tab)JavaScript
console.log([NaN].includes(NaN));console.log([NaN].indexOf(NaN));const map = new Map([[NaN, "stored"]]);console.log(map.get(NaN));const set = new Set([-0]);console.log(set.has(0));
MapSameValueZero

new Map([[NaN, "stored"]]).get(NaN) returns stored.

Try it yourself

Map keys use SameValueZero, so a NaN key can be retrieved with another NaN.

A tiny free-play companion to the recorded SameValueZero run.
Four algorithms on famous pairs
Pair=====Object.isSameValueZero
NaN / NaNfalsefalsetruetrue
-0 / +0truetruefalsetrue
"1" / 1falsetruefalsefalse
null / undefinedfalsetruefalsefalse

Where you'll use this

Most real code uses the same small set of choices. Use strict equality for ordinary comparisons. Use value == null when you intentionally mean “missing as null or undefined.” Use Object.is when your logic is sensitive to NaN or the sign of zero. React uses Object.is for some state comparisons, which is why this detail occasionally appears in UI debugging.

Practical equality choicesPop out in the code editor (opens in a new tab)JavaScript
if (user.middleName == null) {  showOptionalNameField();} if (Object.is(nextTemperature, previousTemperature)) {  skipChartUpdate();} const uniqueIds = new Set(idsFromSeveralLists);

Collections do another job for you. If you dedupe values with new Set(values), it uses SameValueZero, so duplicate NaN values collapse and -0/+0 count as one value.

Common misconceptions

“Strict equality means mathematical equality.”

It is close for normal numbers, but NaN and signed zero follow JavaScript's strict-equality rules, not school math.

“Loose equality is unpredictable.”

It is predictable, but the ordered conversions are too easy to misread. Trace the algorithm, or avoid it except for clear patterns.

“Objects with the same shape are equal.”

Two object literals can look identical and still be different references. Equality does not do a deep property comparison.

“Object.is is always stricter ===.”

It is pickier about signed zero and friendlier about NaN. It is different, not simply stricter.

“includes and indexOf search the same way.”

They differ for NaN: includes uses SameValueZero; indexOf uses strict equality.

Which judge says true?
  • "1" and 1 are true
  • null and undefined are true
  • NaN and NaN are true
  • [NaN].includes(NaN) is true
  • -0 and +0 are true
  • new Set([-0]).has(0) is true
  • obj and obj are true without conversion
  • Object.is(-0, +0) is false
Try it yourself
0 of 8 correct

Sort each statement by the rule that explains why it is true. Some statements are true under more than one judge; choose the judge named by the card's context.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Practice: equality & sameness

5 EXERCISES
Exercise 1 · Warm-upPredict three NaN checks

Predict the three printed booleans, in order.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
console.log([NaN].includes(NaN));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeWalk through "0" == false

    Write the final printed value, then explain the conversion chain to yourself.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    console.log("0" == false);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFix a NaN check

      Replace the body so the function identifies real NaN values without treating the string "NaN" as NaN.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function isActuallyNaN(value) {
        return value === NaN;
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · ChallengeDedupe values including NaN

        Predict the first printed value, then explain why the unique list still includes NaN.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const values = [NaN, NaN, -0, 0, "0", "0"];
        const unique = [...new Set(values)];
        console.log(unique.length);
        console.log(unique.includes(NaN));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeExplain signed zero

          In one sentence, explain why Object.is(-0, 0) is false even though -0 === 0 is true.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          console.log(Object.is(-0, 0));
          console.log(1 / -0);

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            8 QUESTIONS

            Answer from the rulebook, not from vibes. Every option explains itself.

            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8Which equality check should be your default in ordinary application code?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does "1" == 1 print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("1" == 1);

              Choose an answer to see the explanation.

            3. Question 3 of 8What does NaN === NaN print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(NaN === NaN);

              Choose an answer to see the explanation.

            4. Question 4 of 8What does Object.is(-0, 0) print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(Object.is(-0, 0));

              Choose an answer to see the explanation.

            5. Question 5 of 8Which operation uses SameValueZero?

              Choose an answer to see the explanation.

            6. Question 6 of 8What does null == undefined print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(null == undefined);

              Choose an answer to see the explanation.

            7. Question 7 of 8What does [NaN].indexOf(NaN) print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log([NaN].indexOf(NaN));

              Choose an answer to see the explanation.

            8. Question 8 of 8Which expression is a good intentional use of loose equality?

              Choose an answer to see the explanation.

            Key takeaways

            • Default to ===: same type, same value, no conversion.
            • == follows an ordered conversion algorithm; use it deliberately, especially for value == null.
            • Object.is is SameValue: NaN matches itself, and -0 differs from +0.
            • SameValueZero powers includes, Map, and Set: NaN matches and zero signs merge.
            • Object equality is reference equality, not a deep comparison of properties.

            Remember the one-liner.
            JavaScript has four sameness judges: strict avoids conversion, loose converts, Object.is notices NaN and signed zero, and SameValueZero powers collections.

            Up next: Checking types reliably.

            CompleteFrontend Clear concepts. Working examples.