JavaScript coercion experiment
`[] + []` is an empty string. `[] + {}` is an object tag. `{} + []` is zero, but only in a console. All of it follows one algorithm.
- Level
- DEVELOPER
- Read time
- 07 min
- Experiment
- Available
- Type
- INTERACTIVE
The question
Is JavaScript coercion arbitrary, or is there a single spec algorithm that explains every famous example?
Hypothesis
Every case reduces to ToPrimitive, followed by the rules for + and for relational and equality comparison. No exceptions, no magic.
Method
Laboratory available
The instrument for this investigation runs in the laboratory, where the controls, the live model and the observation log share one workstation.
Enter the laboratoryPick an expression, predict the result, then expand the spec trace. The bench evaluates the real expression in your browser and shows the algorithm steps beside it.
What we observed
The + operator is the only arithmetic operator that is also string concatenation. It calls ToPrimitive on both operands with no hint, and if *either* result is a string, it concatenates. Arrays stringify by joining, so [] + [] is "" + "".
Why it happens
ToPrimitive(input, hint), simplified
- 01If the input is already a primitive, return it.
- 02If it has a Symbol.toPrimitive method, call it with the hint and return the result.
- 03For hint 'string': try toString(), then valueOf().
- 04For hint 'number' or 'default': try valueOf(), then toString().
- 05If neither returns a primitive, throw a TypeError.
| Expression | Result | Why |
|---|---|---|
| [] + [] | "" | Both stringify to "", concatenation wins |
| [] + {} | "[object Object]" | "" + the object's default string tag |
| {} + [] | 0 | Only in a console: the leading {} parses as a block, so this is +[] → 0 |
| [] == false | true | Both sides → 0 via ToNumber |
| null == undefined | true | Special-cased in the spec; they equal each other and nothing else |
| null == 0 | false | No numeric conversion is performed for null |
| NaN === NaN | false | IEEE-754, use Object.is or Number.isNaN |
| "2" > "10" | true | Both strings → lexicographic comparison, not numeric |
| 0.1 + 0.2 === 0.3 | false | Binary floating point, not coercion at all |
Further research
Abstract equality (==) has a short, learnable table, but === plus explicit conversion communicates intent, and linters flag == for the same reason surgeons label their instruments. The one defensible use is x == null, which tests for null *or* undefined in a single comparison.
References
- 01ECMA-262ToPrimitive
- 02ECMA-262IsLooselyEqual (abstract equality)
- 03MDNEquality comparisons and sameness