Każdy szanujący się programista JS wie, że jeśli chce wykonać operację arytmetyczną na dwóch zmiennych i co najmniej jedna z nich jest undefined lub obiektem, to wynikiem takiego działania będzie NaN.
1 + undefined // NaN
undefined / 2 // NaN
3 * {} // NaN
undefined / 2 // NaN
3 * {} // NaN
Tak samo będzie, jeśli operacja nie jest dodawaniem i żadnej z liczb nie da się sparsować do liczby (parseInt lub parseFloat).
1 - 'jeden' // NaN
'dwa' / 1 // NaN
'dwa' / 1 // NaN
Infinity
dzielenie przez 0 lub przez null daje już inny wynik - Infinity.
3 / null // Infinity
4 / 0 // Infinity
4 / 0 // Infinity
Wartości logiczne
Wartości logiczne (true i false) są traktowane w operacjach arytmetycznych jak... 1 i 0...
3 / false // Infinity
3 / true // 3
8 * false // 0
8 * true // 8
3 / true // 3
8 * false // 0
8 * true // 8
Niebezpieczny string
Dodawanie, w którym jedną z wartości jest string jest zamieniane na łączenie stringów. Druga wartość, nieważne, jaką ma oryginalną wartość, też jest transformowana do stringa
1 + '1' // "11"
1 + 'dwa' // "1dwa"
null + 'undefined' // "nullundefined"
1 + 'dwa' // "1dwa"
null + 'undefined' // "nullundefined"
ale, każda inna operacja arytmetyczna już działa inaczej
3 * '4' // 12
Tablice
Jeśli operacja arytmetyczna jest dodawaniem, to wszystko co między nawiasami kwadratowymi jest traktowane, jak string. Literalnie wszystko.
2 + [3] // "23"
2 + [3, 4] // "23,4"
2 + [3, 4] // "23,4"
Zagnieżdżanie struktur nic nie daje:
2 + [3, [4, 'aaa', true, null, { a: 1}]] // "23,4,aaa,true,,[object Object]"
Jeśli chcemy wykonać operację arytmetyczną inną niż dodawanie i co najmniej jedna ze zmniennych będzie tablicą, to sprawa się komplikuje. Jeśli tablica ma tylko jeden element i jego wartość jest typem prostym, to ta wartość jest brana pod uwagę w operacji arytmetycznej.
2 * [3] // 6
2 * ['4'] // 8
3 / [0] // Infinity
['cztery'] * 2 // NaN
2 * ['4'] // 8
3 / [0] // Infinity
['cztery'] * 2 // NaN
Przy operacji arytmetycznej innej niż dodawanie, jeśli tablica ma więcej elementów, to wynik jest zawsze NaN.
wyjątki:
[true] - 1 zwraca NaN chociaż true - 1 daje 0.
3 / [] zwraca Infinity choć [][0] daje undefined
Jak się chronić przed błędami?
Jeśli operacje arytmetyczne mają dużą wagę i muszą być wykonywane w JS jedynym działającym sposobem jest sprawdzanie typu zmiennych na których dokonujemy operacji.
Funkcje typu isFinite lub isNaN są niestety ułomne.
isFinite(true) // true
isNaN('') // false
isNaN('') // false
Jeśli czekamy na liczby, które nie są wynikiem innej operacji arytmetycznej operator typeof załatwia sprawę
typeof true === 'number' // false
typeof undefined === 'number' // false
typeof null === 'number' // false
typeof '1' === 'number' // false
typeof undefined === 'number' // false
typeof null === 'number' // false
typeof '1' === 'number' // false
Jeśli natomiast wchodzące dane są wynikiem innych operacji arytmetycznych (czyli ich wartość moze być NaN), to trzeba pamiętać, że:
typeof NaN === 'number' // true
więc należy sprawdzić, czy to co wchodzi nie jest NaN
isNaN(NaN) // true