Suggest an editImprove this articleRefine the answer for “Loose (==) vs strict (===) equality in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Strict equality (`===`)** compares value and type without conversion. **Loose equality (`==`)** converts operands to a matching type first, then compares. ```javascript 5 === "5"; // false - different types 5 == "5"; // true - string converts to number null == undefined; // true - special rule ``` **Key rule:** use `===` by default. The one accepted exception is `if (value == null)`, which catches both `null` and `undefined` in a single check.Shown above the full answer for quick recall.Answer (EN)Image**Strict equality (`===`)** checks value and type without any conversion. **Loose equality (`==`)** converts operands to a matching type first, then compares the results. ## Theory ### TL;DR - `===` is like matching exact shoe size and color; `==` stretches the shoe to fit any foot - `===` skips all type conversion; `==` runs the Abstract Equality Comparison algorithm - `5 === "5"` is false; `5 == "5"` is true (string converts to number) - `null == undefined` is true by spec; `null === undefined` is false - Default rule: use `===` everywhere; `==` is acceptable only for `null/undefined` checks ### Quick example ```javascript console.log(5 === 5); // true console.log(5 === "5"); // false - number vs string, no conversion console.log(5 == "5"); // true - "5" converts to 5 console.log(true === 1); // false - boolean vs number console.log(true == 1); // true - true converts to 1 console.log(null == undefined); // true - special rule in the spec console.log(null === undefined);// false - different types ``` `===` stops at the type check. `==` keeps going and converts. ### Key difference `===` uses the SameValue algorithm: compare type tags first, then values. No conversion, no surprises. `==` triggers the Abstract Equality Comparison, which runs ToNumber on strings, converts booleans to numbers (true to 1, false to 0), and handles objects via `valueOf`/`toString`. That chain is why `[] == 0` is true: `[]` calls `toString()` to get `""`, then `""` converts to `0` via ToNumber. ### When to use - Any normal comparison: use `===` - Checking for `null` or `undefined` together: `value == null` catches both in one line - Comparing user input to a number after `parseInt`: use `===` explicitly - Legacy code where coercion is intentional: `==` with a comment explaining why ### Comparison table | Aspect | `===` (Strict) | `==` (Loose) | |---|---|---| | Type check | Yes, type + value | No, converts first | | Coercion | None | Automatic (string to number, etc.) | | Predictability | High | Low | | Performance | Slightly faster | Slightly slower | | `5 == "5"` | false | true | | `null == undefined` | false | true | | When to use | All comparisons | Only `value == null` | ### How it works internally V8 implements `===` as a direct type-tag comparison followed by a value check. No allocations, no conversions. For `==`, the engine runs ToPrimitive on objects (calling `valueOf` then `toString`), then ToNumber on strings. The coercion chain can go three steps deep before settling on a result, which is why edge cases like `[] == 0` feel counterintuitive. ### Common mistakes **Using `==` in a conditional expecting a single type:** ```javascript // Wrong - "0" and [] also match if (userInput == 0) { ... } // Fix if (userInput === 0) { ... } ``` **Checking an empty array with `==`:** ```javascript // Wrong - never true, object references differ if (data == []) processData(); // Fix if (data.length === 0) processData(); ``` **Forgetting that `NaN` is never equal to itself:** ```javascript console.log(NaN === NaN); // false console.log(NaN == NaN); // false - same rule applies // Fix Number.isNaN(value); // correct check ``` **Loop condition with string input:** ```javascript // Wrong - string "1" matches, loop runs unexpectedly while (count == 1) count--; // Fix while (count === 1) count--; ``` ### Real-world usage - React: `props.id === expectedId` in component guards - Express: `req.params.id === 'new'` in route handlers - Node.js: `process.env.NODE_ENV === 'production'` - Redux: `action.type === types.FETCH_SUCCESS` - ESLint rule `eqeqeq` enforces `===` across a codebase automatically ### Follow-up questions **Q:** Why does `[] == 0` return true? **A:** `[]` calls `toString()` to get `""`, then `""` converts to `0` via ToNumber. So `[] == ""` and `"" == 0`, making `[] == 0` true. **Q:** Is `null == undefined` true? Why? **A:** Yes, it is a special case defined in the Abstract Equality Comparison spec. Only `null` and `undefined` equal each other via `==`; neither equals `0`, `false`, or `""`. **Q:** `NaN === NaN` is false. How do you check for NaN? **A:** Use `Number.isNaN(value)`. The `x !== x` trick also works, since NaN is the only value that fails equality against itself. `Object.is(value, NaN)` is another option. **Q:** Does `==` ever belong in production code? **A:** One case: `if (value == null)` catches both `null` and `undefined` in one comparison. Some teams use it deliberately for that pattern. Outside that, `===` is the safe default. **Q:** What does `Object.is()` add over `===`? **A:** Two edge cases: `Object.is(NaN, NaN)` is true (unlike `===`), and `Object.is(+0, -0)` is false (unlike `===`). For most code `===` is fine, but `Object.is` matches the SameValue spec exactly. ## Examples ### Basic coercion behavior ```javascript // Strict equality - type must match console.log(5 === 5); // true console.log(5 === "5"); // false console.log(false === 0); // false // Loose equality - coercion in action console.log(5 == "5"); // true - string to number console.log(false == 0); // true - false to 0 console.log("" == 0); // true - empty string to 0 console.log("0" == false); // true - both become 0 ``` `==` skips conversion only when both sides already share the same type. ### React component pattern ```javascript function UserCard({ userId, isActive }) { // Strict check - prevents "0" from being treated as a missing prop if (userId === undefined) return <div>No user</div>; // Safe null check with == - catches both null and undefined if (isActive == null) return <div>Status unknown</div>; return <div>User {userId} is {isActive ? "active" : "inactive"}</div>; } // process.env pattern - always strict const isProd = process.env.NODE_ENV === "production"; ``` I started defaulting to `===` everywhere after a loop ran one extra iteration because an API returned the string `"1"` instead of the number `1`. The type mismatch was invisible until I added a `typeof` check. One character (`==` to `===`) would have caught it immediately.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.