JavaScript Comparison Operators: Double vs Triple Equals

== and === both compare values, but only one is safe. Learn how type coercion works with ==, why === is the better default, and the few cases where == is acceptable.

5 min read

JavaScript double equals (==) and triple equals (===) both compare two values, but they behave very differently. Strict equality checks whether two values are the same without converting anything. Loose equality tries to convert the values to the same type before comparing, which can produce surprising results.

javascriptjavascript
console.log(0 === false); // false (number vs boolean, no match)
console.log(0 == false);  // true  (false is converted to 0, then 0 == 0)

This single example explains why strict equality is the safer default.

How === (Strict Equality) Works

Strict equality checks two things: the value and the type. If the types are different, it returns false immediately without trying to convert anything:

javascriptjavascript
console.log(5 === 5);       // true  (same type, same value)
console.log(5 === "5");     // false (number vs string, no conversion)
console.log(true === 1);    // false (boolean vs number)
console.log(null === undefined); // false (different types)
console.log(0 === false);   // false (number vs boolean)

Strict equality is predictable. Every comparison in the example above behaves exactly as you would expect from looking at the types.

How == (Loose Equality) Works

Loose equality tries to convert both values to the same type before comparing. This process is called type coercion:

javascriptjavascript
console.log(5 == "5");      // true  (string "5" becomes number 5)
console.log(true == 1);     // true  (true becomes 1)
console.log(false == 0);    // true  (false becomes 0)
console.log(null == undefined); // true  (special rule for null/undefined)
console.log("" == 0);       // true  (empty string becomes 0)
console.log([] == 0);       // true  (empty array becomes "" then 0)
console.log([] == "");      // true  (empty array becomes "")

Each of these results follows coercion rules, but most developers find them surprising on first encounter. The rules are consistent, but they are not intuitive.

The Coercion Rules for ==

When you use loose equality, JavaScript follows these steps:

== (loose equality) coercion algorithm

The algorithm tries to coerce values into matching types, step by step, until it can make a comparison. This chain of conversions is what produces the unexpected results.

The == Surprises Table

ExpressionResultWhy
0 == falsetruefalse becomes 0, then 0 == 0
"" == falsetrueBoth become 0
"" == 0true"" becomes 0
"0" == falsetrueBoth become 0
whitespace string == 0trueWhitespace string becomes 0
[] == 0true[] becomes "" then 0
[1] == 1true[1] becomes "1" then 1
[1,2] == "1,2"trueArray becomes comma-separated string
null == 0falsenull only equals null and undefined
undefined == 0falseundefined only equals null and undefined

If this table makes you uncomfortable, that is the right reaction. Most of these results are consequences of coercion rules, not expressions anyone would intentionally write. With strict equality, every expression in the table except direct value matches would return false.

The One Case for ==

There is one idiomatic use of loose equality that is widely accepted: checking for both null and undefined at once:

javascriptjavascript
if (value == null) {
  // value is either null or undefined
}

This is equivalent to the more explicit strict version below, which spells out both checks instead of relying on the special loose-equality rule:

javascriptjavascript
if (value === null || value === undefined) {
  // same check, more verbose
}

Because null == undefined is true (a special rule in the spec) and neither equals anything else with loose equality, value == null is a clean shorthand. For all other comparisons, use strict equality.

Strict Inequality: !== vs !=

The same rules apply to inequality. !== is the strict version, and != is the loose version:

javascriptjavascript
console.log(5 !== "5"); // true  (strict: different types, so they are not equal)
console.log(5 != "5");  // false (loose: "5" becomes 5, then 5 == 5)

Always prefer strict inequality for the same reasons you prefer strict equality.

Object and Array Comparisons

Both loose and strict equality compare objects and arrays by reference, not by content:

javascriptjavascript
console.log({} === {});       // false (different objects in memory)
console.log([] === []);       // false (different arrays in memory)
console.log({} == {});        // false (same rule applies to ==)
console.log([] == []);        // false
 
const a = [1, 2, 3];
const b = a;
console.log(a === b);         // true (same reference)

This is true for both loose and strict equality. If you need to compare the contents of two arrays or objects, you need a different approach.

What About NaN?

NaN is the only value in JavaScript that is not equal to itself:

javascriptjavascript
console.log(NaN === NaN); // false
console.log(NaN == NaN);  // false

Use Number.isNaN() to check for NaN. For more, see the NaN guide.

The Practical Rule

Use strict equality and strict inequality for every comparison. The only exception is value == null as a shorthand for value === null || value === undefined. This rule eliminates an entire category of bugs caused by unexpected type coercion. For an overview of all operators, see the operators guide.

Enable the eqeqeq rule in ESLint to enforce this automatically in your projects.

Rune AI

Rune AI

Key Insights

  • === checks both value and type without converting anything.
  • == converts types before comparing, which can produce unexpected results.
  • Use === and !== as your default comparison operators.
  • The only practical use for == is checking == null to catch both null and undefined.
  • Objects and arrays are compared by reference with both == and ===.
  • NaN === NaN is false. Use Number.isNaN() to check for NaN.
RunePowered by Rune AI

Frequently Asked Questions

Should I ever use == in JavaScript?

Almost never. The only common case where == is acceptable is checking for both null and undefined at once: if (value == null). This is equivalent to if (value === null || value === undefined). Beyond that, always use ===.

Why does JavaScript have both == and ===?

== (loose equality) came first in the original language design. === (strict equality) was added later because == causes too many surprising results from type coercion. === has been the recommended default for over two decades.

Is === faster than ==?

Yes, but the difference is tiny. === skips the type coercion step entirely, so it is technically faster. The real reason to use === is correctness, not performance.

Conclusion

=== (strict equality) compares value and type without conversion. == (loose equality) converts types before comparing, which leads to surprising results like 0 == false being true. Always use === and !== as your default. The only reasonable exception is value == null, which cleanly checks for both null and undefined at once.