1. WTF JS Explanation - Primitives

1. WTF JS Explanation - Primitives

W tym artykule poruszamy primitive values w kontekście wyrażeń algebraicznych.

W JS mamy następujące znaki działań - #operations-table, i niezależnie z którego operatora byśmy skorzystali (+, -, *, /), pod spodem obsługa wyrażeń algebraicznych działa dokładnie tak - #sec-applystringornumericbinaryoperator. Metoda wpierw sprawdza czy występuje operator dodawania, potem ewentualne konwertuje nasze wartości do ToPrimitive i dalej do ToString - lub w przypadku innych operatorów zawsze następuje konwersja do ToNumeric.

Przeanalizujmy wpierw sytuację przy dodawaniu - następuje ToPrimitive na obu wartościach.

"abc" + true // chcemy dodać do siebie dwie wartości - string'a i boolean'a

Zauważ, że w ToPrimitive nasz input jest traktowany różnie w zależności od typu (primitive jest od razu zwracany, a Object dalej obsługiwany - co omówimy w oddzielnym artykule).

Tak więc w przypadku primitive nasze wartości są przekazywane dalej. Metoda ApplyStringOrNumericBinaryOperator sprawdza czy któraś z wartości jest typu String - jeśli tak to na obu primitive value leci ToString - a co za tym idzie w ostatecznym dodawaniu następuje konkatencja stringów!!

"abc" + true // ToPrimitive("abc") + ToPrimitive(true)
ToPrimitive("abc") + ToPrimitive(true) // ToString("abc") + ToString(true)
"abc" + true // "abc" + "true" --> "abctrue" (konkatenacja)

W przypadku ToString jest to właściwie odpalenie konstruktora String na naszej wartości.

Przedstawia się to następująco dla naszych podstawowych typów prostych:

  • boolean (true / false) -> String(true) -> "true" (w przypadku false "false")
  • number -> String(1) -> "1"
  • undefined -> String(undefined) -> "undefined"
  • null -> String(null) -> "null"
  • string -> String("abc") -> "abc"

Jeśli oba primitive value nie są typu String lub działaniem nie jest dodawanie to następuje ToNumeric na naszych wartościach (i tak naprawdę dzieje się tak dla wszystkich innych typów).

true + false // chcemy dodać do siebie dwie wartości typu boolean

W przypadku ToNumeric pod spodem odpalane jest tak naprawdę ToNumber.

Czyli dokładnie tak:

true + false // ToPrimitive(true) + ToPrimitive(false)
ToPrimitive(true) + ToPrimitive(false) // toNumeric(true) + toNumeric(false)
toNumeric(true) + toNumeric(false) // ToNumber(true) + ToNumber(false)
true + false // 1 + 0 --> 1

W przypadku ToNumber jest to właściwie odpalenie konstruktora Number na naszej wartości.

Przedstawia się to następująco dla naszych podstawowych typów prostych:

  • boolean (true / false) -> Number(true) -> 1 (w przypadku false 0)
  • number -> Number(1) -> 1 (i zgodnie ze specyfikacją do typu number zaliczamy all possible Number values including the special “Not-a-Number” (NaN) value, positive infinity (+Infinity), and negative infinity (-Infinity))
  • undefined -> Number(undefined) -> NaN
  • null -> Number(null) -> 0
  • string -> Number("1") -> 1 - tutaj tak naprawdę na naszej wartości robione jest Number::toString - co mniej więcej oznacza, że jeśli argumentem jest Number Type w postaci string'a to tak naprawdę jest traktowane jako Number
  • string -> Number("text") -> NaN - każdy string niebędący Number Type będzie NaN

Wiedząc już, że należy rozpoznać typ naszego primitive value ORAZ działanie (+, - itd.) myślę, że całkowicie jasne są dla Ciebie poniższe przykłady na typach prostych:

true + false // 1 + 0 --> 1
undefined + false // NaN + 0 --> NaN
"abc" + true // "abc" + "true" --> "abctrue"
"abc" - false // NaN - 0 --> NaN
5 + null // 5 + 0 --> 5
"NaN" - false // NaN - 0 --> NaN
"NaN" + false // "NaN" + "false" --> "NaNfalse"

Ciekawostka! W przypadku większej ilości składowych wyrażenia należy wykonywać operacje od lewej do prawej. Np. true + "true" + true = "truetruetrue" lub true + true + "true" = "2true".

W kolejnych artykułach omówię wyrażenia algebraiczne na typach złożonych.