Skip to content
JavaScript Lab · LAB-03ToPrimitive / valueOf

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 laboratory

Pick 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

  1. 01If the input is already a primitive, return it.
  2. 02If it has a Symbol.toPrimitive method, call it with the hint and return the result.
  3. 03For hint 'string': try toString(), then valueOf().
  4. 04For hint 'number' or 'default': try valueOf(), then toString().
  5. 05If neither returns a primitive, throw a TypeError.
ExpressionResultWhy
[] + []""Both stringify to "", concatenation wins
[] + {}"[object Object]""" + the object's default string tag
{} + []0Only in a console: the leading {} parses as a block, so this is +[] → 0
[] == falsetrueBoth sides → 0 via ToNumber
null == undefinedtrueSpecial-cased in the spec; they equal each other and nothing else
null == 0falseNo numeric conversion is performed for null
NaN === NaNfalseIEEE-754, use Object.is or Number.isNaN
"2" > "10"trueBoth strings → lexicographic comparison, not numeric
0.1 + 0.2 === 0.3falseBinary floating point, not coercion at all
Famous specimens, explained

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

  1. 01ECMA-262ToPrimitive
  2. 02ECMA-262IsLooselyEqual (abstract equality)
  3. 03MDNEquality comparisons and sameness