How == differs from === in JavaScript
The classic explanation says == checks values and === checks both type and value. The spec says something different - both check the type. This note is about what the actual difference is.
These four lines look innocent, but the popular story about their difference is incomplete:
1 == "1"; // true
1 === "1"; // false
1 == 1; // true
1 === 1; // trueThe version you’ve heard most often: == looks at the value, === looks at the type and value. It explains the first two lines, but it doesn’t tell the truth about how the operators work under the hood. Both check the type. The real difference lies somewhere else.
#What === actually does
The === (strict equality) operator works in the simplest possible way:
- If the two sides have different types - return
false. Done. - If the types match - compare the values.
No conversion, no special path for objects, no algorithm running quietly in the background. 1 === "1" is false because the types differ. 1 === 1 is true because the types match and the values match too.
#What == actually does
The == (loose equality) operator looks like a different beast, but the spec has something the first paragraph wouldn’t lead you to expect. Its algorithm - IsLooselyEqual - starts with a single line:
If SameType(x, y) is true, then return IsStrictlyEqual(x, y).In other words: when the types are the same, == simply runs === - it’s the same engine operation, just spelled differently in your code.
The whole difference only kicks in when the types are different. At that point === returns false immediately, while == tries to reconcile the two sides through conversion - that’s coercion. The idea: if you want to compare a number with a string, the engine will handle that for you. 1 == "1" returns true because the engine turns "1" into 1 and compares the numbers.
That’s the whole difference. One operator allows coercion, the other refuses it.
#Three rules that fall out of this one difference
Know your types. If you know what’s on both sides, the choice of operator comes down to whether you want conversion. If you don’t know - neither operator will save you. The problem is earlier, in the code itself.
When the types match - it doesn’t matter. userId === otherUserId and userId == otherUserId are the same call. No surprises on the == side, because there’s no coercion to run.
x == null is the one exception worth remembering. The only case where == gives you cleaner code than ===. In a single expression it checks whether x is null or undefined, and only that - no other value will sneak in as true.
#What about performance
There’s a common belief that one of these operators is noticeably faster. In practice V8 will optimize both versions to the same form when the types match. It’s true that == on different types adds the coercion algorithm - a few more steps than ===, which returns false straight away. But the cost is measured in nanoseconds per operation. You pick an operator to express intent in the code, not to shave milliseconds.
#Closing thought
== and === differ in exactly one thing: whether they allow coercion when the types don’t line up. The rule “always use ===” is a good default - it removes the question “do I know the types here?” from your head. But that doesn’t make == a trap. It’s a tool you don’t reach for when you don’t know the types. Shorter: know your types.
What exactly the engine does inside that “try to reconcile the sides” step - that is, how coercion works step by step - I covered in the coercion note.