Two equals signs instead of three, one character less, and a radically different behavior. This is the operator that earned JavaScript its reputation as an unpredictable language, and the reputation is deserved.
Definition
Loose equality compares two values after converting them to a common type. When both operands share a type, it behaves exactly like Strict equality (===). All the trouble comes from the cases where the types differ.
console.log("2" == 2); // true: the string becomes a number
console.log(2 === 2); // true, with no conversion
// The opposite is written !=
console.log("2" != 2); // falseThis automatic conversion has a name, Coercion (type conversion), and it follows precise rules that almost nobody remembers by heart. That is the whole problem: an operator whose answers require memorizing a table.
What the conversion actually produces
| Comparison | Result | Why |
|---|---|---|
"" == 0 | true | The empty string becomes zero |
"0" == 0 | true | The string becomes the number zero |
"" == "0" | false | Two strings, no conversion |
[] == false | true | Both end up at zero |
null == 0 | false | null only compares with undefined |
The first three rows are enough to demonstrate the problem: the operator is not transitive. The empty string equals zero, the string "0" equals zero, and yet the two strings are not equal to each other. No intuition survives that.
The fourth row deserves a full look: an empty array is true inside an if, and yet it equals false with two equals signs. The two mechanisms are unrelated, the first converts to a boolean, the second to a number.
The one use still recommended
One exception draws a consensus, even among people who ban this operator everywhere else.
const value = null;
// Tests null AND undefined in a single go
if (value == null) {
console.log("nothing to handle"); // prints
}
// The strict equivalent, longer
if (value === null || value === undefined) { }Comparing against null also catches undefined, and nothing else: not zero, not the empty string, not false. It is the only place where two equals signs are safer than three, because they make it impossible to forget one of the two empty values.
Frequently asked questions
Why does [] == ![] return true?
Because two conversions run one after the other. Negation first turns the array on the right into false, since an object is always true. That leaves an array and a boolean, both converted to numbers: zero on one side, zero on the other. So the comparison is true.
Should the operator be banned in a project?
That is the default stance of linters, with a configurable exception for the comparison against null. The setting is a good compromise: it removes accidental conversions while keeping the short form that genuinely helps.
How do I compare a form input with a number?
By converting explicitly rather than letting the operator do it. A form field always returns text: apply Number(input) or parseInt, check the result, then compare with Strict equality (===). The code gets longer and far more predictable.