Operatory i konwersje typów. Wstęp do pętli

OPERATORY ARYTMETYCZNE - poniżej najczęściej wykorzystywane:

OPERATOR DODAWANIA


KONKATENACJA - łączenie łańcuchów znaków.


OPERATOR ODEJMOWANIA


OPERATOR MNOŻENIA


OPERATOR DZIELENIA

OPERATOR MODULO - zwraca resztę z dzielenia:

OPERATOR POTĘGOWANIA


JavaScript, kiedy widzi w stringach liczby to będzie próbował w przypadku operatorów odejmowania, mnożenia i dzielenia konwertować typy zmiennych do liczb.

True + True --> Javascript czyta "True" jako "1", dlatego gdy będziemy próbować zrobić dodawanie i wynik będzie 2

Porównania LUŹNE - automatycznie konwertują typy danych. Oznaczane dwoma znakami równości ==

Porównania ŚCISŁE - porównuje wartość i typ danych, bez automatycznej konwersji. Oznaczane trzema znakami równości ===

Porównanie nierówności - też występują luźne lub ścisłe.

Nerówność LUŹNA - automatycznie konwertują typy danych. Oznaczane wykrzyknikiem i znakiem równości !=

Nierówność ŚCISŁA - porównuje wartość i typ danych, bez automatycznej konwersji. Oznaczana wykrzyknikiem i dwoma znakami równości !==

Operatory relacyjne


KONWERSJA - JAWNA (EXPLICIT) I NIEJAWNA (IMPLICIT)

parseInt() - konwersja

Number.isNan() - test czy dana konwersja jest możliwa.

Aby przetestować, czy wartość w JavaScript jest NaN (Not a Number), należy użyć funkcji isNaN(), ponieważ NaN jest jedyną wartością, która nie jest równa samej sobie (NaN !== NaN). Alternatywnym, niezawodnym sposobem jest użycie porównania x !== x. Nigdy nie należy używać zwykłego porównania === do sprawdzania NaN, ponieważ zawsze zwróci ono false
Sposoby sprawdzania NaN

Użyj funkcji isNaN(): Jest to najbardziej powszechny i zalecany sposób.

isNaN("to nie liczba"); // true
isNaN(NaN); // true
isNaN(5); // false

Konwersja do Boolean. Wartości Truthy i Falsy
Wszystkie istniejące wartości Falsy:
- false
- 0 (zero)
- -0 (minus zero)
- 0n (zero n - wartość związana z BigInt)
- " " (pusto, brak jakiegokolwiek znaku czy liczby)
- null
- undefined
- NaN

Wartości Truthy - wszystkie inne niż powyższe, np:
- true
- 1
- -1
- "0"
- "false"
- " " (spacja)
- "tekst"

Lepiej używać operatorów ścisłych oraz jawnej konwersji typów. Jest to bezpieczniejsze, zwłaszcza przy dłuższych kodach.


OPERATORY LOGICZNE

Pozwalają na stworzenie złożonych warunków i podejmowanie decyzji w kodzie na podstawie tych warunków:

Operator AND (&&)
Zwraca wartość true tylko wtedy jeżeli obydwa operandy są Truthy.
Czy wszystkie warunki są spełnione równocześnie

Operator OR (||)
Zwraca wartość true kiedy PRZYNAJMNIEJ JEDEN z operandów jest Truthy.
Często

Operator NOT (!)

Łączenie operatorów arytmetycznych i logicznych jest jak najbardziej możliwe, na przykład wykorzystywane w sklepach internetowych z alkoholem.

Operatory logiczne nie zawsze będą zwracać wartości True/False, czasami mogą też zwracać wartość któregoś z operandów.

SHORT-CIRCUIT EVALUATION, przykłady:

Praktyczne użycie AND:

Praktyczne użycie OR:

NULLISH COALESCING OPERATOR: ??
Operator ?? w JavaScript to operator koalescencyjny (nullish coalescing operator), który zwraca prawy operand, jeśli lewy operand jest null lub undefined, w przeciwnym razie zwraca lewy operand. Jest to bezpieczna alternatywa dla operatora OR (||), który zwraca prawy operand także dla innych "fałszywych" wartości, takich jak null, undefined, 0, '' (pusty ciąg znaków) czy false

  • Co robi: Zwraca pierwszy nie-null i nie-undefined operand.
  • Główny cel: Używany do przypisania wartości domyślnej zmiennej, jeśli jej pierwotna wartość nie została jeszcze ustawiona lub jest null albo undefined.
  • Różnica między ?? a ||:

??: Działa tylko dla null i undefined.

||: Działa dla wszystkich "fałszywych" wartości (null, undefined, 0, '', false, NaN). 

Przykład:

let uzytkownik = { imie: "Anna" };
let nazwisko = uzytkownik.nazwisko ?? "Kowalski"; // nazwisko będzie "Kowalski"
let wiek = 0;
let wiekDziecka = wiek ?? 1; // wiekDziecka będzie 0, bo wiek nie jest null ani undefined

OPERATOR OPTIONAL CHAINING: ?.
Operator ten reprezentowany jest przez znak zapytania i kropki ?.:

const obj = {
  person: null,
}
console.log(obj.person?.name)

Za jego pomocą możemy wykonać proste sprawdzenie, czy kolejne właściwości obiektu istnieją, zanim nastąpi próba ich odczytania. Widzimy w tym przypadku, że zanim odwołamy się do pola name to operator sprawdza wcześniej, czy obiekt person istnieje. Jeżeli właściwość, do której się odwołujemy nie istnieje to zwracana jest wartość undefined i nie zostanie zwrócony błąd przez JavaScript.

Obiekt person nie musi być też koniecznie obiektem o wartości nullowej:

const obj2 = {
  person: '',
}
console.log(obj.person?.name) // undefined

Optional Chaining sprawdza po kolei ścieżkę odwołania i jeżeli nie uda mu się pobrać kolejnego odwołania, zwracana jest wartość undefined. Dla tego operatora nie ma znaczenia, do jakich pól się odwołujemy, operator ten sprawdza czy możliwy jest łańcuch wywołania, który zapisaliśmy w wyrażeniu.

Gdybyśmy chcieli zrobić to w tradycyjny sposób, użylibyśmy na przykład operatora AND:

console.log(obj.person && obj.person.name) // null

Optional Chaining ma zdecydowaną przewagę, zapewniając prosty i czytelny kod.

Możemy sobie dodatkowo wyobrazić, że pracujemy z obiektami z jakiegoś zewnętrznego API i nigdy nie wiadomo co do nas przyjdzie, a obiekty takie zazwyczaj są mocno zagnieżdżone w sobie:

response = {};
const res = response.data?.user?.adress?.postcode

Czasami musimy sprawdzić dosłownie każdą właściwość obiektu, zanim pobierzemy z niej jakaś wartość. Z tym operatorem robimy to w bardzo eleganckiej formie. Już pozostawiam Wam wyobrażenie sobie, jak wiele warunków trzeba by sprawdzić z operatorem AND.

Optional Chaining działa również z funkcjami i tablicami.

Optional Chaining z tablicami:

PIERWSZEŃSTWO OPERATORÓW = OPERATOR PRECEDENCE

Najwyższą wagę ma operator: NOT, potem AND, potem OR.

W przypadku operatorów arytmetycznych obowiązują zasady matematyki:
Najpierw nawiasy, potem mnożenie i dzielenie, na końcu dodawanie i odejmowanie.

Nowe operatory, z 2021 roku:

&&= przypisuje wartość tylko jeśli zmienna jest truthy
||= przypisuje wartość tylko jeśli zmienna jest falsy
??= przypisuje wartość tylko jeśli zmienna jest null lub undefined.

!!! DO ZAPAMIĘTANIA !!!
- jeśli używany short Circuit i poprawną wartością jest "0", "false albo pusty string to zamiast "OR" to należy używać Nullish Coalescing Operator (??)

- jeśli musimy w sposób bezpieczny dostać się do właściwości obiektu to używamy Optional Chaining (?.)

- pamiętaj o pierwszeństwie operatorów: operator precedence!
!!! DO ZAPAMIĘTANIA !!!

WYRAŻENIA WARUNKOWE:

Kolejność warunków ma znaczenie! Drzewo decyzyjne będize działać aż znajdzie pierwszy prawdziwy warunek, reszty nie będzie czytać.

Warunki złożone w "else if"

Jeśli chciemy procesować więcej niż jeden warunek na raz, to potrzebujemy więcej niż jednego IF-a.

Nie przesadzać z ilością zagnieżdzęć w instrukcjach warunkowych. Więcej niż 3 poziomy zagnieżdżenia mogą nie być już czytelne. Komentuj kod, żeby wyjażniać dlaczego dana instrukcja warunkowa się pojawiła. To w przyszłości pomoże.
-----------------------------------------------------------------------------------------

SWITCH

Instrukcja switch w JavaScript służy do wyboru i wykonania jednego z wielu bloków kodu w zależności od wartości wyrażenia. Działa poprzez ścisłe porównanie podanej wartości ze stałymi w poszczególnych blokach case i wykonanie kodu powiązanego z pierwszym pasującym case. Słowo kluczowe break jest używane do wyjścia z instrukcji po dopasowaniu, a default służy jako domyślny przypadek, gdy żadna z poprzednich opcji nie pasuje. Jak działa switch

  • Ocena wyrażenia: Program ocenia wyrażenie podane w switch (np. zmienną).
  • Porównanie case: Wartość wyrażenia jest porównywana (za pomocą ścisłego porównania ===) z wartością każdej z instrukcji case.
  • Wykonanie kodu:
    • Jeśli zostanie znalezione dopasowanie (case), program wykonuje kod znajdujący się w tym bloku case.
    • Jeśli nie użyto break, program będzie kontynuował wykonywanie kodu we wszystkich kolejnych blokach case, nawet jeśli nie pasują one do warunku.
    • Po wykonaniu kodu z pasującego bloku, break przerywa wykonywanie instrukcji switch i przenosi program do dalszej części kodu.
    • Jeśli żadne z case nie pasuje, wykonany zostanie blok default, jeśli istnieje.
  • Brak break: Pominięcie break może prowadzić do tzw. "przewijania" (fall-through), gdzie wykonany zostanie kod z kolejnych case.
  • default: Służy jako "else" i jest opcjonalny. Wykonuje się, gdy żadne z innych case nie pasuje do wartości wyrażenia. 

Przykład użyciajavascript

let dzienTygodnia = "poniedziałek";
let nazwaDnia;

switch (dzienTygodnia) {
  case "poniedziałek":
    nazwaDnia = "Dzień roboczy";
    break; // To kluczowe słowo przerywa wykonywanie
  case "sobota":
  case "niedziela":
    nazwaDnia = "Dzień wolny";
    break;
  default:
    nazwaDnia = "Inny dzień";
}

console.log(nazwaDnia); // Wynik: Dzień roboczy


Ostatni case w Switchu to często "default" (wykonuje się wtedy, kiedy żaden z poprzednich case'ów nie pasuje).

Kiedy Switch, a kiedy If Else?
Używaj if-else do porównywania zmiennej lub wyrażenia w odniesieniu do kilku różnych warunków, które są niezależne od siebie. Używaj switch, gdy masz wiele warunków, które opierają się na jednej zmiennej lub wyrażeniu i chcesz wybrać jeden blok kodu z wielu możliwych opcji, co może zwiększyć czytelność i wydajność. Kiedy używać if-else

  • Niezależne warunki: Kiedy masz wiele warunków, które nie są ze sobą powiązane. Na przykład, sprawdzanie, czy liczba jest dodatnia, ujemna, czy zerowa.
    • if (x > 0)
    • else if (x < 0)
    • else
  • Porównania złożone: Gdy potrzebujesz porównać różne typy danych lub zastosować bardziej złożone porównania.
    • if (user.role === 'admin' && user.isActive)
    • else if (user.role === 'editor' || user.lastLogin > oneWeekAgo)
  • Różne bloki kodu: Kiedy chcesz wykonać różne bloki kodu w zależności od tego, czy warunek jest spełniony, czy nie.

Kiedy używać switch

  • Jedna zmienna: Gdy chcesz wybrać jeden blok kodu na podstawie wartości jednej zmiennej lub wyrażenia.
  • Wiele przypadków: Kiedy masz wiele możliwych wartości do porównania dla tej samej zmiennej, np. wybieranie akcji na podstawie numeru dnia tygodnia.
    • switch (day) { case 1: ...; break; case 2: ...; break; ... }
  • Lepsza czytelność: Gdy wielokrotne instrukcje if-else if stają się długie i trudne do przeczytania, switch może być bardziej przejrzysty.
  • Wydajność: W niektórych przypadkach silniki JavaScript mogą zoptymalizować switch do szybszego wykonywania w porównaniu do długich sekwencji if-else, ponieważ kompilator może od razu przejść do odpowiedniego przypadku. 

Przykład porównania:

// Przykład z if-else
let ocena = 85;
if (ocena >= 90) {
    console.log("Doskonale");
} else if (ocena >= 80) {
    console.log("Bardzo dobrze");
} else if (ocena >= 70) {
    console.log("Dobrze");
} else {
    console.log("Przeciętnie");
}

// Ten sam przykład z switch
let ocena = 85;
switch (true) {
  case (ocena >= 90):
    console.log("Doskonale");
    break;
  case (ocena >= 80):
    console.log("Bardzo dobrze");
    break;
  case (ocena >= 70):
    console.log("Dobrze");
    break;
  default:
    console.log("Przeciętnie");
}

Kiedy wybrać switch nad if-else

  • Gdy porównujesz wartość zmiennej z wieloma stałymi wartościami (stringami, liczbami).
  • Gdy chcesz uzyskać czytelniejszy kod przy wielu warunkach opartych na tej samej zmiennej.
  • Gdy potencjalna wydajność jest kluczowa, ponieważ kompilator może zoptymalizować instrukcje switch do szybszego wykonywania. 


Switch może działać z wyrażeniami:

Switch fall-through

OPERATOR TRÓJARGUMENTOWY (TERNARY)

Operator trójargumentowy (ternary) w JavaScript działa na zasadzie sprawdzenia warunku i zwrócenia jednej z dwóch wartości. Składa się z warunku, znaku zapytania (?), dwukropka (:) oraz dwóch wyrażeń: jednego, które jest wykonywane, gdy warunek jest prawdziwy (przed dwukropkiem), i drugiego, które wykonuje się, gdy warunek jest fałszywy (po dwukropku). Jest to zwięzła alternatywa dla prostych instrukcji if...else. Jak działa

  • Skladnia: warunek ? wyrażenieJeśliPrawda : wyrażenieJeśliFałsz.
  • Ocena warunku: Najpierw oceniany jest warunek.
  • Rezultat:
    • Jeśli warunek jest true, zwracana jest wartość wyrażenieJeśliPrawda.
    • Jeśli warunek jest false, zwracana jest wartość wyrażenieJeśliFałsz

Przykładjavascript

let wiek = 20;
let status = (wiek >= 18) ? "pełnoletni" : "niepełnoletni";

// W tym przypadku:
// 1. Warunek: wiek >= 18 (20 >= 18) jest prawdziwy (true).
// 2. Operator zwraca wartość po znaku zapytania: "pełnoletni".
// 3. Zmienna status otrzymuje wartość "pełnoletni".



Ternary w wyrażeniach:

TU SPRAWDZAMY CZY RESZTA Z DZIELENIA JEST RÓWNA ZERU (CZY LICZBA JEST PARZYSTA):

LICZBA % 2 - - - 0


PĘTLE

Pętle robimy po to, żeby móc powtórzyć ten sam kawałek kodu określoną liczbę razy albo w jakiejś sekwencji wartości.
Klasyczna pętla składa się z trzech części:
- Warunku początkowego
- Warunku do spełnienia
- ikrementacji

Najprostsz pętla to PĘTLA FOR:

Zmienna let w powyższym przypadku jest specyficzna dla tego konkretnego bloku kodu.
" i " w pętli to iterator. Jeśli umieszczamy pętlę w pętli to trzeba inaczej nazwać iterator, żeby nie doszło do nadpisywania. Zwykle używa się po prostu kolejnej litery alfabetu (czyli " j " ).


Iteracja wstecz:


Iteracja po tablicy:

Sumowanie wartości z pomocą pętli:
suma += i dodaje "i" do sumy i podaje wynik.


Pętla do wyszukiwania w tablicy:

Pamiętaj, że nie zawsze musimy ikrementować po 1 (czyli zapis: "i++").
Możemy iterować po takiej wartości, po jakiej chcemy, np: "i +=7" będzie iterował co 7 element.


PĘTLA WHILE:
Wykonuje warunek tak długo jak jest prawdziwy.
While wymaga ręcznych inicjalizacji i zarządzania iteracjami bo inaczej dostaniemy pętlę nieskończoną.

"licznik--" zmniejsza wartości o 1.

While z flagą kontrolną:
Mamy tablicę. i chcemy przejśćprzez wszystkie elementy tablicy. Dlatego indeks ustawiamy na 0.
Ustalamy warunek, który ma być spełniany. Jak długo będzie działał tak długo będzie działała pętla.
Poniżej mamy dwa warunki:
pętla ma działać tak długo jak indeks jest w scope tablicy i póki wrunek drugi jest "nie znaleziono". Kiedy znajdziemy element i warunek się zmieni na True to pętla przestanie działać:


Przykład gdy while się nie wykona:



PĘTLA "DO... WHILE"
Zawsze wykona się minimum raz:

Gdzie nmożna użyć? {rzy wpisywaniu przez użytkownika hasła, żeby dać możliwość wprowadzenia użytkownikowi kilka razy hasła (aż mu się uda wprowadzić prawidłowe albo żeby zablokować mu dalsze próby).

While wykona suę tylko wtedy gdy warunek jest wpeniony.
Do While najpierw wykona kod, a potem dopiero sprawdzi warunek. Dlatego jest wygodnym rozwiązaniem do walidacji danych wprowadzanych przez użytkownika.

WHILE Z BREAKIEM:


Break: wczesne zakończenie pętli:

Break: pominięcie konkretnej iteracji:
Robi się to przed dodatkwy warunek w pętli i słowo-klucz "Continue".
Poniżej przykład jak pomijać liczby parzyste i wyświetlać same nieparzyste z użyciem "continue"

Inny przykład:

Jeśli mamy zagnieżdżone pętle to breaki działają też na konkretne pętle:


PODSUMOWANIE

Pętle forwhile i do...while w JavaScript służą do wielokrotnego wykonywania bloku kodu, ale różnią się składnią, mechanizmem działania oraz typowymi scenariuszami użycia.Oto główne różnice i zastosowania każdej z nich:

1. Pętla for
Pętla for jest najczęściej używaną pętlą w JavaScript, idealną, gdy dokładnie wiesz, ile razy chcesz wykonać pętlę (np. iteracja po elementach tablicy).

  • Składnia: Posiada wbudowaną strukturę do inicjalizacji licznika, definiowania warunku kontynuacji oraz aktualizacji licznika w jednej linii.
  • Gdzie używać:
    • Iteracja przez elementy tablicy.
    • Wykonanie kodu określoną, z góry znaną liczbę razy.
    • Standardowe iteracje, gdzie potrzebujesz łatwego dostępu do indeksu (i)
for (let i = 0; i < 5; i++) {
  // Ten kod wykona się 5 razy (dla i = 0, 1, 2, 3, 4)
  console.log("Numer iteracji: " + i);
}

2. Pętla while

Pętla while jest bardziej elastyczna i powinna być używana, gdy nie wiesz z góry, ile razy pętla ma się wykonać, ale zależy to od spełnienia określonego warunku.

  • Składnia: Sprawdza warunek przed wykonaniem bloku kodu. Jeśli warunek jest fałszywy od początku, kod wewnątrz pętli nigdy się nie wykona.
  • Gdzie używać:
    • Pobieranie danych dopóki serwer nie zwróci statusu "gotowe".
    • Gry, gdzie pętla trwa "dopóki gracz żyje".
    • Gdy warunek zakończenia jest dynamiczny i nieoparty na prostym liczniku.
let licznik = 0;
while (licznik < 3) {
  // Ten kod wykona się 3 razy
  console.log("Licznik wynosi: " + licznik);
  licznik++; // Pamiętaj o inkrementacji, inaczej pętla będzie nieskończona!
}


3. Pętla do...while

Pętla do...while jest bardzo podobna do while, ale z jedną kluczową różnicą: gwarantuje wykonanie bloku kodu co najmniej raz, nawet jeśli warunek początkowy jest fałszywy.

  • Składnia: Wykonuje blok do co najmniej raz, a następnie sprawdza warunek while.
  • Gdzie używać:
    • Wszędzie tam, gdzie wymagane jest wykonanie początkowej akcji (np. wyświetlenie menu użytkownikowi), a następnie kontynuowanie lub zakończenie pętli w zależności od danych wejściowych.
    • Obsługa interfejsu użytkownika, gdzie najpierw musisz coś pokazać, a potem czekać na interakcję.
let liczba;
do {
  // Ten kod wykona się co najmniej raz, zanim sprawdzi warunek
  liczba = Math.random();
  console.log("Wylosowana liczba: " + liczba);
} while (liczba < 0.5);
// Pętla trwa dopóki liczba jest mniejsza niż 0.5

Podsumowanie różnic

Cechaforwhiledo...while
Kiedy używać?Gdy znasz liczbę iteracji.Gdy warunek jest zmienny; sprawdza przed.Gdy musisz wykonać kod min. raz.
Sprawdzenie warunku?Przed każdą iteracją.Przed każdą iteracją.Po pierwszej iteracji.
Ryzyko pętli nieskończonej?Małe (jeśli licznik jest poprawny).Duże (łatwo zapomnieć o aktualizacji zmiennej).Duże (łatwo zapomnieć o aktualizacji zmiennej).