Text in, numbers out
User input and file data arrive as strings. Python used int("42") and float("3.5"). JavaScript gives you:
Number("42")→42. Strict: the whole string must be numeric, otherwise you getNaN.parseInt("42px", 10)→42. Forgiving: reads leading digits and stops. The10means base ten, always pass it.parseFloat("3.5kg")→3.5. Same idea with decimals.
NaN ("not a number") is the result of failed number math. It is contagious (NaN + 1 is NaN) and weird: it is not even equal to itself. Test for it with Number.isNaN(x), never with ===.
Going the other way, toFixed formats with a fixed number of decimals: (3.14159).toFixed(2) gives "3.14". Careful: it returns a string, ready for display, not for more math.
One more classic: 0.1 + 0.2 prints 0.30000000000000004. Computers store decimals in binary, so tiny rounding errors appear. Python does the exact same thing. Format with toFixed when showing money.
Strict conversion, forgiving conversion, and rounding
Five lines covering the whole lesson: the difference between Number and parseInt, what a failed conversion produces, the binary rounding surprise, and the fix for display.
console.log(Number("42")); console.log(Number("abc")); console.log(parseInt("42px", 10)); console.log(0.1 + 0.2); console.log((0.1 + 0.2).toFixed(2));
Output
42 NaN 42 0.30000000000000004 0.30
Number("42") succeeds because every character is part of a number, while Number("abc") fails and produces NaN. parseInt("42px", 10) reads digits from the front and stops at the first character that cannot belong, which is what makes it useful for values like CSS sizes and forgiving for values you would rather have rejected.
The fourth line is the famous one. 0.1 and 0.2 cannot be stored exactly in binary, any more than one third can be written exactly in decimal, so their sum lands a hair away from 0.3. The fifth line rounds it for display, and note that 0.30 keeps its trailing zero because toFixed produces text rather than a number.
Adding two prices that arrived as text
Prices from a form or a file are strings, and this is the safe order of operations: convert first, add second, format last.
const a = "19.99"; const b = "5.50"; const total = Number(a) + Number(b); console.log(total.toFixed(2));
Output
25.49Converting first is not optional, because + between two strings means concatenation. Skipping the conversions would give "19.99" + "5.50", which is the string "19.995.50", a value that looks broken but raises no error at all.
The toFixed(2) at the end handles the display, and calling it last is deliberate. All arithmetic happens on real numbers, and formatting is the final step before the value is shown, which keeps rounded intermediate values from creeping into later math.
What toFixed hands back
For const price = (9.5).toFixed(2);, the value of typeof price is "string". The result is the text "9.50", not the number 9.50.
That distinction has real consequences, because + on a string concatenates instead of adding. Continuing to calculate with a toFixed result gives values like "9.502" where 11.5 was expected, and again with no error to point at the cause.
The discipline that avoids it is to keep the two phases separate.
| Phase | Work with | Example |
|---|---|---|
| input | Number(...) or parseFloat(...) | Number("19.99") |
| calculation | plain numbers | subtotal * 1.07 |
| display | toFixed, template literals | ` $${total.toFixed(2)} ` |
Testing for a failed conversion needs
Number.isNaN(x)rather thanx === NaN.NaNis the one value in JavaScript that is not equal to itself, so the===comparison is alwaysfalse.