piątek, 22 lutego 2019

świadome operacje arytmetyczne na zmiennych w JS

NaN

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

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

Infinity

dzielenie przez 0 lub przez null daje już inny wynik - Infinity.

3 / null // 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

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"

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"

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

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

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

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