
Parsování čísel v JavaScriptu: správně a bezpečně
Funkce pro práci s čísly parseInt
, Number
, NaN
, doporučení a ukázky.
Práce s čísly v JavaScriptu přináší velkou porci zábavy. Některé věci se hodí znát pro běžnou praxi. Další jsou spíš kuriosity vhodné k šikanování uchazečů o práci na pohovorech.
Většina zvláštních situací plyne z automatické typové konverse (tzv. type coercion), která často způsobuje, že číslo a řetězec „spolu komunikují“.
Při pokusu o sčítání platí, že pokud je alespoň jeden operand řetězec, dojde ke zřetězení:
Operand je hodnota nebo proměnná, na které operátor provádí svou operaci.
"1" + 2 // "12"
2 + "1" // "21"
"1" + "2" // "12"
1 + 2 + "3" // "33" (nejprve 1+2=3, pak 3+"3" → "33")
"1" + 2 + 3 // "123" (nejprve "1"+2 → "12", pak "12"+3 → "123")
U ostatních operací (-
, *
, /
) se řetězce převádějí na čísla, pokud to jde:
"10" - 1 // 9
"6" * "7" // 42
"12" / 3 // 4
"foo" * 2 // NaN
Této skutečnosti je možné využívat i pro převod na čísla, kdy se řetězec vynásobí jedničkou nebo se přidá +
před číslo v řetězci.
+"1" + 1 // 2
Skoro všechno je string
Reálné problémy plynoucí z míchání řetězců (string) s čísly (number) vyplývají z toho, že skoro všechno může být řetězec:
-
Všechny hodnoty zadané do formulářů jsou řetězce. Ani číselný
<input type="number>
není výjimkou. -
URL parametry / query string jsou na tom stejně:
const params = new URLSearchParams(window.location.search); const id = params.get("id"); console.log(typeof id); // "string"
-
I u JSONu získaného z API je běžnou praxí, že jsou čísla posílána jako řetězce.
const data = JSON.parse('{"age":"42"}'); console.log(typeof data.age); // "string"
Součet čísel vs. součet řetězců
Hodnota z <input type="text">
je řetězec. Operace x + 5
provede zřetězení, nikoliv sčítání.
x + y
(řetězení)Number(x) + Number(y)
+x + +y
parseInt(x, 10) + parseInt(y, 10)
parseFloat(x) + parseFloat(y)
Co s tím?
Number
Obecně typicky stačí převést takový vstup pomocí Number
:
const age = Number(document.querySelector('#age')?.value ?? '')
const id = Number(new URLSearchParams(location.search).get('id') ?? '')
const data = JSON.parse('{"age":"42","items":[{"qty":"3"},{"qty":"5"}]}')
const age2 = Number(data.age)
A zde začíná další zábava. Datům od uživatele není dobré věřit. Aplikace by si měla poradit s každým vstupem.
Funkce Number
je relativně přísná. Poradí si s bílými znaky okolo čísla (to může být i nezalomitelná mezera, takže Number("\u00A042")
nebo Number("42\u00A0")
je v pořádku). Prázdný řetězec nebo null
převede na 0
, jinak je výsledkem NaN (not a number):
Number(" 42 ") // 42
Number("") // 0
Number(" ") // 0
Number(null) // 0
Number(undefined) // NaN
Number("42px") // NaN
Number("\n\t3.14 ") // 3.14
Number("42e4") // 420000
Number("0xFF") // 255
Number("0b1010") // 10
Number("0o17") // 15
Funkce Number
převede na číslo i zápisy s exponentem (42e4
). Stejně tak si podle prefixu dokáže zvolit jinou číselnou soustavu – šestnáctkovou/hexadecimální (prefix 0x), dvojkovou/binární (0b) nebo osmičkovou/oktalovou (0o).
parseFloat
/parseInt
Pro trochu tolerantnější přístup k číslům jde použít parseFloat
/parseInt
(pro parsování čísla s desetinnou čárkou nebo celého).
Hlavní rozdíl je v tom, že tyto funkce dokáží odstranit nepořádek na konci čísla. Takže třeba v pohodě odstranit jednotky:
parseInt("42px", 10) // 42
parseFloat("3.14em") // 3.14
parseFloat("1.2e3ms") // 1200
parseInt("08", 10) // 8
parseInt("0x10", 16) // 16
parseInt("0x10", 10) // 0
Další specialita je možnost zvolit číselnou soustavu, ve které se má číslo parsovat. To je považováno za doporučený postup, protože bez jejího uvedení se ji může prohlížeč pokusit hádat. Zvlášť historicky to způsobovalo nekonsistentní situace.
parseInt("1010", 2) // 10
parseInt("ff", 16) // 255
parseInt("z", 36) // 35
parseInt("08") // 8 nebo 0 (historicky), proto vždy parseInt(s, 10)
Number(x)
+x
parseInt(x, 10)
parseFloat(x)
Number.isFinite(Number(x))
Number.isNaN(Number(x))
Číselná soustava (radix)
Radix je základ číselné soustavy. parseInt(text, radix)
říká, v jaké soustavě se má řetězec číst. 10 je desítková, 2 binární, 16 šestnáctková; povolený rozsah je 2…36. Znaky a
až z
představují hodnoty 10 až 35 a nerozlišují velikost písmen. Funkce čte zleva a zastaví se na prvním nepovoleném znaku.
parseInt("1010", 2) // 10
parseInt("ff", 16) // 255
(255).toString(16) // "ff"
parseInt("08", 10) // 8
parseInt(s, base)
platné znaky
n.toString(base)
Validace čísel
Z různých specifik čísel jako je možnost zadat exponent nebo použít různé číselné soustavy plyne, že prostá validace, jestli je vstup od uživatele číslo, nemusí být dostatečná.
Je tak potřeba zvolit pro konkrétní případ, jestli přijímat zápisy s exponentem, zápisy v jiné číselné soustavě nebo třeba nekonečno Infinity
.
K úvaze je i použití serialisace místo validace, kdy se neplatné znaky ve vstupu ignorují a algoritmus se snaží pochopit, co chtěl člověk zadat.
Zde může být typicky problém s oddělovači tisíců nebo desetinných míst.
Lokalisovaná čísla: čárka a mezery
JavaScript parsuje desetinnou tečku. Vstup jako "1 234,56"
je potřeba převést na "1234.56"
. Zároveň je vhodné odstranit různé druhy mezer v tisících (NBSP
, narrow NBSP
, běžná mezera).
function parseCzDecimal(input) {
const raw = String(input)
const withoutSpaces = raw.replace(/[\u00A0\u202F\s]/g, "")
const unified = withoutSpaces.replace(",", ".")
return toNumberStrict(unified)
}
parseCzDecimal("1 234,56") // 1234.56
parseCzDecimal("12,0") // 12
parseCzDecimal("12 345") // 12345
Udělat políčko tolerantní k českému číslu by šlo tímto způsobem.
Bez mezer
Tečka místo čárky
Number(...)
Validní tvar
K úvaze je, jestli není validace až moc přísná, že si neporadí naopak s anglickým formátem 1,234.56
. Je potřeba to dobře vyzkoušet pro konkrétní případ.
Okrajové hodnoty a další typy
- Number("") → 0, Number(null) → 0, Number(undefined) → NaN.
- parseInt/parseFloat:
parseInt("")
,parseInt(null)
,parseInt(undefined)
→ NaN (protože""
,"null"
,"undefined"
nejsou čísla). - +x: chová se jako
Number(x)
. - Boolean("") → false, Boolean(null) → false, Boolean(undefined) → false.
- Boolean:
Number(true) → 1
,Number(false) → 0
. - Pole:
[] → "" → 0
,[1] → "1" → 1
,[1,2] → "1,2" → NaN
. - Objekt:
{} → "[object Object]" → NaN
. - Symbol:
Number(Symbol())
aparseInt(Symbol())
vyhodí TypeError. - BigInt:
Number(10n) → 10
, pozor na přesnost u velkých hodnot.
typeof x
String(x)
Number(x)
+x
parseInt(x, 10)
parseFloat(x)
Boolean(x)
Doporučení
- Chcete‑li zvalidovat, že celý vstup je číslo, použijte
Number
aNumber.isFinite
. - Potřebujete‑li číslo vyčíst z počátku textu (např.
"42px"
), použijteparseInt(x, 10)
neboparseFloat
. - Vždy předejte
radix
(číselnou soustavu) doparseInt
:parseInt(s, 10)
. - Pro lokální formáty (1 234,56) nejprve vstup normalisujte (mezery, NBSP, čárka → tečka) a pak použijte přísnou konversi.
Související články


JavaScript null
a undefined
Rozdíly mezi null
a undefined
v JavaScriptu, kdy je používat a jak se vyhnout běžným chybám.

Sleep v JavaScriptu
Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promise
a async
/await

JavaScript Battery API
Jak v JS zjistit stav baterie, co dnes funguje a kdy API nepoužívat.