Blog
javascript
2 min read

Don't Trust ==: JavaScript's Weird Comparison Truths (== vs === vs Object.is)

Use === as the default, and switch to Object.is only when you need to handle NaN or signed zeros (+0 vs -0) precisely. Avoid == at all costs.

Don't Trust ==: JavaScript's Weird Comparison Truths

Always use === as your default comparison operator. Only switch to Object.is when you need to handle NaN or signed zeros (+0 vs -0) precisely. Avoid == to prevent unexpected type coercion.


Background: The Chaos of Type Coercion

The double equals ( ==) operator attempts to compare values by forcibly converting them to the same type. This leads to infamous "JavaScript quirks" where empty arrays equal zero, but non-empty strings might not.

javascript
// The quirks of ==\n[] == 0;         // true\n\"\" == 0;         // true\nnull == undefined; // true\n\n// But...\n\"0\" == 0;       // true\n\"0\" == [];      // false (Wait, what?)

The Three Levels of Comparison

1) Loose Equality (==)

Performs "Abstract Equality Comparison". It is unpredictable and generally considered a bad practice because it can lead to subtle bugs.

2) Strict Equality (===) - THE STANDARD

Performs "Strict Equality Comparison". No type conversion allowed. If types differ, it returns false. This is what you should use 99% of the time.

Exception: NaN === NaN is false, and 0 === -0 is true.

3) Same-Value Equality (Object.is)

The most precise comparison. It fixes the quirks of === and mirrors how memory handles these specific values.

javascript
Object.is(NaN, NaN); // true\nObject.is(0, -0);   // false (Correctly identifies signed zero)

Implementation (Interactive Demo)

This demo shows how Object.is behaves differently compared to === when dealing with edge cases like NaN.

javascript
\"use client\"\n\nimport { useState } from 'react';\n\nexport default function ComparisonDemo() {\n  const [val1, setVal1] = useState(NaN);\n  const [val2, setVal2] = useState(NaN);\n\n  const isStrictEqual = val1 === val2;\n  const isSameValue = Object.is(val1, val2);\n\n  return (\n    <div className=\"p-4 border rounded-lg bg-gray-50 flex flex-col gap-2\">\n      <p>Comparing <code>NaN</code> and <code>NaN</code>:</p>\n      <ul className=\"list-disc pl-5\">\n        <li>Strict (===): <span className=\"text-red-500 font-bold\">{String(isStrictEqual)}</span></li>\n        <li>Object.is: <span className=\"text-green-500 font-bold\">{String(isSameValue)}</span></li>\n      </ul>\n    </div>\n  );\n}

Summary & Best Practices

  1. Default to ===: Protects against unexpected type coercion.
  2. Use Object.is for Edge Cases: Essential for complex state management where NaN or -0 needs to be distinguished accurately.
  3. Delete == from your memory: Unless you have a specific reason to allow type coercion (even then, explicit conversion is better).

Conclusion

JavaScript's comparison rules can be confusing, but by following a "strict-first" approach, you can eliminate an entire class of subtle bugs. Object.is provides the ultimate precision when the standard operators fall short.


References

Related Posts