Coercion: what +, Number() and sort do to your values
The conversions JavaScript performs without asking — string concatenation, Number versus parseInt, the default sort, and floating-point sums.
After this lesson you can
- Predict the result of + and - when one side is a string
- Choose between Number(), parseInt() and a stricter check for user input
- Explain why [10, 9, 1].sort() is wrong and 0.1 + 0.2 is not 0.3
Lesson 1 covered ==. This lesson covers the other places JavaScript
converts a value for you: arithmetic, the explicit conversion functions,
and sorting. Coercion is not random — every case follows a rule — but the
rules are easy to misremember, so it pays to know the handful that bite.
+ is two operators in one
+ adds numbers, and joins strings. If either side is (or converts to) a
string, it joins. Every other arithmetic operator — -, *, /, % —
only works on numbers, so it converts both sides to numbers.
"5" + 2; // "52"
"5" - 2; // 3
1 + 2 + "3"; // "33" — left to right: 1 + 2 first, then joined
"1" + 2 + 3; // "123" — a string from the start, so everything joins
Objects are first turned into a primitive. A plain object becomes
"[object Object]", an array becomes its elements joined with commas,
and an empty array becomes "". That is why [] + [] is an empty string.
export function run() { return { stringPlusNumber: "5" + 2, stringMinusNumber: "5" - 2, numbersThenString: 1 + 2 + "3", stringThenNumbers: "1" + 2 + 3, arrayPlusArray: [] + [], arrayPlusObject: [] + {}, arrayToString: String([1, [2, 3]]), defaultSort: [10, 9, 1].sort(), numericSort: [10, 9, 1].sort((a, b) => a - b), pointOneTwo: 0.1 + 0.2, isPointThree: 0.1 + 0.2 === 0.3, };}Number() against parseInt()
Number(s) converts the whole string or gives NaN. parseInt(s, 10)
reads digits from the start and stops at the first character it does not
understand, ignoring the rest. Neither one is a validator on its own:
export function run() { const inputs = ["42", " 42 ", "", " ", "12px", "2.5", "1e3", "0x10", "abc"]; // each row: [input, Number(input), parseInt(input, 10)] return inputs.map((s) => [s, Number(s), parseInt(s, 10)]);}The results worth remembering: Number("") and Number(" ") are 0,
not NaN, so an empty form field quietly becomes zero. Number("1e3") is
1000 and Number("0x10") is 16, because both are valid number
literals. parseInt("12px", 10) is 12, which is handy for CSS and wrong
for anything you are validating. Always pass the radix to parseInt.
The default sort compares strings
Array.prototype.sort() without a comparator converts every element to a
string and compares the strings, so [10, 9, 1].sort() gives
[1, 10, 9] — "10" comes before "9" the same way "ab" comes before
"b". For numbers, pass a comparator: (a, b) => a - b.
Floating-point sums
Every number is a 64-bit binary float. 0.1 has no exact binary
representation, so is . Money is stored
as integer cents; other comparisons use a tolerance chosen for the size
of the values ( is the gap just above , so it is far
too small a tolerance for large numbers). Every integer up to
(2^53 − 1) is exact; beyond it some integers
cannot be represented, so use .
Exercise · Pro
Quantities from a form, strictly
Exercises are free in the first 3 lessons of every course. From here on they are part of Pro and Sprint; the lesson and its runnable examples stay open.
Upgrade