Skip to content

Latest commit

 

History

History
283 lines (214 loc) · 10.5 KB

conversion.md

File metadata and controls

283 lines (214 loc) · 10.5 KB

Automatyczna konwersja typów

JavaScript nie wymaga od Ciebie abyś deklarował typ zmiennych. Przykładowo możesz utworzyć zmienną typu liczbowego o nazwie np. someVar, a następnie przypisać jej wartość znakową:

let someVar = 10;
someVar = "to jest napis";

Z jednej strony jest to fajne, bo upraszcza sprawę. Z drugiej może powodować błędy w bardziej skompilowanych skryptach. Dlatego też w każdym większym języku (np C++) konieczne jest określanie typów danych. Dlatego też powstały dla JavaScript takie nakładki jak TypeScript

JavaScript lubi być automatyczny. Tak jest w przypadku operacji na typach prostych i kilku innych momentach (np. w przypadku hoistingu zmiennych czy deklaracji funkcji). Ta automatyczność przejawia się tym, że w wielu momentach JavaScript chce nam ułatwić życie automatycznie konwertując danych tym zmiennej na inny. Przykładowo dodając liczbę do stringa, liczba zostanie skonwertowana na string. Podobnie dodając string do tablicy (ale nie pushując do niej) tablica zostanie skonwertowana na string.

JavaScript nie jest w stanie dodać do siebie tablic czy obiektów (bo nie robi się tego +, a specjalnymi metodami), ale potrafi dodawać numery czy stringi do siebie. Dlatego podczas operacji często stara się skonwertować "niedodawalne" typy danych na typy, które potrafi dodać - najczęściej są to stringi, ale czasami też numbery czy inne wymagane w danej sytuacji typy danych.

Poniżej znajdziesz kilka przykładów konwersji, a dalej możesz przeczytać o zasadach, które obowiązują przy konwersji.

!!""           // false
+"01"          // 1
"2" + 2        // "22"
'true' == true // false
[] ? "a" : "b"; // "a"
[] == true ? "a" : "b"; // "b"
[] + []        // ""
{} + {}        // [object Object][object Object]
[1,2,3] + {}   // 1,2,3[object Object]

Manualna konwersja na liczby

Zamiast zdawać na automatyczną konwersję typów przez JavaScript możemy samodzielnie wymusić konwersję typu za pomocą w budowanych funkcji Number(), Boolean(), String(), bądź parseInt(), parseFloat(). Oczywiście często możesz się spotkać z praktykami deweloperów, którzy wykorzystują znajomość zasady automatycznych konwersji i konwertują za pomocą operatorów takich, jak +, !! w celach krótszego zapisu:

  • + -> Number(),
  • !! -> Boolean()
Number("1");  // 1    <- konwertuje string na number
Number(true); // 1    <- konwertuje boolean na number
Boolean(1);   // true <- konwertuje number na boolean
String(1);    // "1"  <- konwertuje string na number

parseInt(str, system_liczbowy); //parsuje string na liczbę całkowitą
parseFloat(str); //parsuje string na liczbę

Zasady przy konwersji typów

Zależnie od rodzaju wykonywanej operacji i użytych w niej typach Javascript konwertuje do jednego z typów: String, Number, Boolean. Możemy spotkać się konwersją przy użyciu takich operatorów, jak: logiczny, arytmetyczny, porównawczy czy warunkowy.

!!""           // !!Boolean("")   ->   !!false -> false
+"01"          // +Number("01")   ->  1
"2" + 2        // "2" + String(2) -> "2" + "2" -> "22"
'true' == true // Number('true') == Number(true) -> NaN == 1 -> false
1 ? "a" : "b"; // Boolean(1) ? "a" : "b" -> true ? "a" : "b" -> "a"

Przy próbie konwersji typu złożonego jest wykonywana metoda toPrmitive na typie object. toPrmitive w uproszczeniu wykonuje metodę valueOf albo toString. Nie dotyczy przy konwersji użyciu operatora logicznego i warunkowego.

const a = {};
+a   // a.valueOf() -> +a.toString() -> +"[object Object]" -> +Number("[object Object]") -> NaN

// konwersja przy przysłoniętej metodzie valueOf
const b = {
    valueOf: function(){
      return "3";
    }
  }
-b   // -b.valueOf() -> -"3" -> -Number("3") -> -3

Konwersja przy użyciu operatora logicznego (!, !!, &&, ||)

!!0         // !!Boolean(0)       ->   !!false -> false
!!1         // !!Boolean(1)       ->   !!true  -> true

!!""        // !!Boolean("")      ->   !!false -> false
!!"true"    // !!Boolean("true")  ->   !!true  -> true
!!"false"   // !!Boolean("false") ->   !!true  -> true

!!null      // !!Boolean(null)    ->   !!false -> false
!!undefined // !!Boolean(undefined) -> !!false -> false

!!{}        // !!Boolean({})      ->   !!true  -> true
!![]        // !!Boolean([])      ->   !!true  -> true

!!Symbol()  // !!Boolean(Symbol()) ->  !!true  -> true

Konwersja przy użyciu operatora warunkowego (?)

Oczywiście zasady są takie same przy instrukcji warunkowej: if, switch.

0 ? "a" : "b"         // Boolean(0) ? "a" : "b" -> false ? "a" : "b" -> "b"
1 ? "a" : "b"         // Boolean(1) ? "a" : "b" -> true ? "a" : "b" -> "a"

"" ? "a" : "b"        // Boolean("")      ? "a" : "b" -> false ? "a" : "b" -> "b"
"true" ? "a" : "b"    // Boolean("true")  ? "a" : "b" -> true  ? "a" : "b" -> "a"
"false" ? "a" : "b"   // Boolean("false") ? "a" : "b" -> true  ? "a" : "b" -> "a"

null ? "a" : "b"      // Boolean(null)      ? "a" : "b" -> false ? "a" : "b" -> "b"
undefined ? "a" : "b" // Boolean(undefined) ? "a" : "b" -> false ? "a" : "b" -> "b"

({}) ? "a" : "b"      // Boolean({}) ? "a" : "b" -> true ? "a" : "b" -> "a"
[] ? "a" : "b"        // Boolean([]) ? "a" : "b" -> true ? "a" : "b" -> "a"

Symbol() ? "a" : "b"  // Boolean(Symbol()) ? "a" : "b" -> true ? "a" : "b" -> "a"

Konwersja przy użyciu operatora arytmetycznego (-, + etc.)

+true       // +Number(true) ->  1
+false      // +Number(false) -> 0

+""         // +Number("")   ->  0
+"0"        // +Number("0")  ->  0
+"1"        // +Number("1")  ->  1
+"01"       // +Number("01") ->  1
+"konwersja1" // +Number("konwersja1") -> NaN

+null       // +Number(null) -> 0
+undefined  // +Number(undefined) -> NaN

+{}         // +({}).valueOf() ->  +({}).toString() ->  +"[object Object]" -> +Number("[object Object]") -> NaN
+[]         // +[].valueOf()   ->  +[].toString()   ->  +""    -> +Number("") -> 0
+[1]        // +[1].valueOf()  ->  +[1].toString()  ->  +"1"   -> +Number("1") -> 1
+[1,0]      // +[1,0].valueOf() -> +[1,0].toString() -> +"1,0" -> +Number("1,0") -> NaN

+Symbol()   // throw error: cannot convert a Symbol value to a number

// operacje na więcej niż jednym typie
"2" + 2     // "2" + String(2) -> "2" + "2" -> "22"
2 + "2"     // String(2) + "2" -> "2" + "2" -> "22"
2 + 2 + "2" // 4 + "2" -> String(4) + "2" -> "4" + "2" -> "42"
2 - 2 - "2" // 0 - "2" -> 0  Number("2") -> 0 - 2 -> -2
[] + []    // "" -> bo obie tablice zostały skonwertowane na "" czyli mamy "" + ""
{} + {}     // [object Object][object Object]
[1,2,3] + {}  // 1,2,3[object Object]

Konwersja przy użyciu operatora porównawczego (==)

porównanie z sobą wartości pustych zwraca true

null == undefined      // true
undefined == undefined // true
null == null           // true

porównanie wszystkiego innego z wartościami pustymi zwraca false

null == *      // false
undefined == * // false

wartość NaN nigdy z sobą nie są równe

NaN == NaN    // false
NaN === NaN   // false

Przy luźnym porównaniu string do number. Wartość o typie string zostanie z konwertowana za pomocą operacji Number().

"" == 0     // Number("") == 0    -> 0 == 0   -> true
"0" == 0    // Number("0") == 0   -> 0 == 0   -> true
"1" == 0    // Number("1") == 1   -> 1 == 0   -> false
"abc" == 0  // Number("abc") == 0 -> NaN == 0 -> false

Natomiast przy porównaniu jakiegokolwiek wartości typu prostego do wartości typu Boolean. Obydwa typy przed porównaniem są konwertowane do number za pomocą operacji Number()

false == 0     // Number(false) == 0 -> 0 == 0 -> true
true == 0      // Number(true) == 0  -> 1 == 0 -> false

false == ""    // Number(false) == Number("")    -> 0 == 0   -> false
'true' == true // Number('true') == Number(true) -> NaN == 1 -> false

Przy porównaniu typu prostego Boolean, String bądź Number z object zostanie porównany typu prosty z wynikiem wykonania toPrmitive.

[] == ""       // [].valueOf() == ""  -> [].toString() == ""  -> "" == ""  -> true
[] == "0"      // [].valueOf() == "0" -> [].toString() == "0" -> "" == "0" -> false
[] == 0        // [].valueOf() == 0   -> [].toString() == 0   -> "" == 0   -> Number("") == 0 -> 0 == 0 -> true

[false] == 0   // [false].valueOf() == 0 -> [false].toString() == 0 -> "false" == 0 -> Number("false") == 0 -> NaN == 0 -> false

["0"] == 0     // ["0"].valueOf() == 0   -> ["0"].toString() == 0   -> "0" == 0   -> Number("0") == 0   -> 0 == 0 -> true
["0","0"] == 0 // ["0","0"].valueOf() == 0 -> ["0","0"].toString() == 0 -> "0,0" == 0 -> Number("0,0") == 0 -> NaN == 0 -> false

const a = {};
a == "[object Object]" // a.valueOf() == "[object Object]" -> a.toString() == "[object Object]" -> "[object Object]" == "[object Object]" -> true
a == ""        // a.valueOf() == ""  -> a.toString() == ""   -> "[object Object]" == ""   -> false
a == 0        // a.valueOf() == 0    -> a.toString() == 0    -> "[object Object]" == 0    -> Number("[object Object]") == 0 -> NaN == 0 -> false
a == true     // a.valueOf() == true -> a.toString() == true -> "[object Object]" == true -> Number("[object Object]") == Number(true) -> NaN == 1 -> false

// konwersja przy przysłoniętej metodzie valueOf
const b = {
  valueOf: function(){
    return "1";
  }
}

b == 1       // b.valueOf() == 1    -> "1" == 1    -> Number("1") == 1 -> 1 == 1 -> true
b == true    // b.valueOf() == true -> "1" == true -> Number("1") == Number(true) -> 1 == 1 -> true

Truthy/falsy

Możesz się spotkać takim pojęciem jak false values czy true values. Za tymi pojęciami kryje się lista wartości, które zwracają true albo false przy konwersji za pomocą w budowanej funkcji Boolean().

Poniższe wartości są zawsze 'fałszywe':

  • false
  • 0 (zero)
  • '' or "" (pusty string)
  • null
  • undefined
  • NaN

Oraz:

  • document.all

Każde z pozostałych wartości są zawsze 'prawdziwe', włączając poniższe:

  • '0' (string z wartością zero)
  • 'false' (string z wyrazem “false”)
  • [] (pusta tablica)
  • {} (pusty obiekt)
  • function(){} ('pusta' funkcja)

Źródła