Two Ways to Ask "Are These Equal?"
JavaScript gives you two equality operators: === (strict) and == (loose). They look almost identical. They behave very differently.
=== checks whether both operands are the same type and the same value. == converts operands to a common type first, then compares. That conversion — type coercion — is where most of JavaScript's equality reputation comes from.
The short version: use === by default. The long version is worth reading once so you know what you're opting out of.
Strict Equality: What You Probably Want
=== returns true only when both sides match in type and value. No coercion, no surprises:
If the types don't match, the answer is false immediately. If they do, JavaScript compares the values. For primitives that's a value comparison; for objects it's a reference comparison (more on that in a moment).
!== is the strict inequality operator and follows the same rules in reverse.
Loose Equality: Coercion in Disguise
== lets the two sides have different types. Before comparing, it runs a coercion dance to try to make them match:
The exact rules are in the spec, and they're not awful, but remembering them mid-debug is another matter. "0" == false being true catches even experienced developers. So does [] == false (also true, because the array coerces to "", which coerces to 0).
This is why most style guides — and ESLint's eqeqeq rule — tell you to use === by default. You trade a character of typing for rules you can actually remember.
The One Useful == Pattern
There's a single loose-equality idiom worth knowing: x == null returns true when x is either null or undefined, and false for everything else.
The strict equivalent is x === null || x === undefined, which works but reads noisier. Many codebases allow == null as the one blessed exception. Pick a rule, apply it consistently.
Objects Compare by Reference
For objects, arrays, and functions, both === and == ask the same question: "Do both sides point to the same object in memory?" Not "do they have the same contents?"
Two object literals with identical contents are still two different objects. This trips up everyone at least once.
For value-based comparison you write your own helper, use a library (lodash.isequal), or serialize with JSON.stringify for simple plain objects:
JSON.stringify only works for simple data — it ignores functions, undefined, symbols, and doesn't guarantee key order across engines for all shapes. It's a quick check, not a general solution.
NaN Is Not Equal to Anything
NaN ("not a number") is the value JavaScript hands back when a numeric operation has no meaningful answer — 0/0, Number("abc"), Math.sqrt(-1). Both equality operators return false when either side is NaN, including when both sides are NaN:
To detect NaN, use Number.isNaN(value). Don't use the older global isNaN — it coerces its argument first, so isNaN("hello") returns true, which is almost never what you want.
Object.is: Almost ===, With Two Fixes
Object.is(a, b) behaves like === except for two edge cases:
Most of the time === is what you want. Reach for Object.is when you specifically need to treat NaN as equal to itself, or distinguish +0 from -0 — both rare, both occasionally important in numeric code and in framework internals (React uses Object.is for state comparisons).
Inequality Operators Follow the Same Split
!== is strict, != is loose, and the same advice applies:
Default to !==. If you already allowed == null, you can allow its counterpart != null for the "not null and not undefined" check.
A Checklist for Comparing Things
When you need to compare two values, walk through this:
- Primitives, same type expected? Use
===. - Checking for null or undefined?
x == nullis fine if your style guide allows it; otherwisex === null || x === undefined. - Checking for
NaN?Number.isNaN(x). - Comparing objects by identity?
===does exactly what you want. - Comparing objects by contents? Write a helper, use a library, or serialize. The built-in operators can't help.
Stick to ===, treat == as a special tool for the == null case, and you'll dodge the equality quirks that fill JavaScript FAQ lists.
Next: Operators
Equality is one slice of JavaScript's operator set. The next doc walks through the rest — arithmetic, logical, assignment, and the shorter-form operators you'll lean on in day-to-day code.
Frequently Asked Questions
What's the difference between == and === in JavaScript?
=== is strict equality — it returns true only if both operands have the same type and the same value. == is loose equality — it coerces operands to the same type before comparing. 1 === '1' is false, but 1 == '1' is true because == converts the string to a number first.
Should I always use === in JavaScript?
As a default, yes. === is predictable and the rules are easy to hold in your head. The one common exception is x == null, which conveniently matches both null and undefined. Most linters (ESLint's eqeqeq rule) enforce === and allow that one pattern as an opt-in.
Why does NaN === NaN return false?
The IEEE 754 spec says NaN is not equal to anything, including itself. So every equality operator returns false when NaN is involved. Use Number.isNaN(x) to check if a value is NaN, or Object.is(NaN, NaN) which returns true.
How do I compare two objects for equality in JavaScript?
Both == and === compare objects by reference, not by contents. {a: 1} === {a: 1} is false because they're different objects. To compare by value you need to write your own check, use a library like Lodash's isEqual, or serialize with JSON.stringify for simple plain objects.