# Instrukcje warunkowe

*"Jeżeli kupisz mi zabawkę to nie będę płakał, w przeciwnym razie zrobię aferę na cały sklep."*

Powyższa wypowiedź to typowy warunek, który steruje działaniami rodziców. Pisząc nasze skrypty w zasadzie non stop będziemy stosować podobne warunki, które w naszym przypadku będą sterować przebiegiem naszego programu.

```js
const nr = prompt("Podaj jakąś liczbę");

if (nr > 20) { //jeżeli...
    console.log("Liczba jest większa od 20");
} else { //w przeciwnym razie...
    console.log("Liczba jest mniejsza lub równa 20");
}
```

## Wynik sprawdzenia <a href="#porownywanie" id="porownywanie"></a>

Gdy porównujemy ze sobą dwie wartości (tak jak powyżej zmienną `nr` z liczbą 20), wynikiem takiej operacji zawsze będzie wartość Boolean, czyli **true** lub **false** (prawda/fałsz):

```js
const a = 10;
const b = 20;

console.log(b > a); //true
console.log(b < a); //false
console.log(b === a); //false
```

```js
const result = a > b;
console.log(result); //false
```

```js
const a = 10;
const b = 20;

if (a < b) { //kod się wykona
    console.log("A jest mniejsze od B");
}
```

```js
const nr = prompt("Podaj jakąś liczbę");
if (nr > 5) {
    console.log(`Liczba ${nr} jest większa od 5`);
}
```

Ostatni listing pokazuje charakterystyczną rzecz, z którą nie raz będziemy się stykać w Javascript. Pod zmienną `nr` pobraliśmy liczbę, którą wpisze nam w okienko użytkownik. Wartość pobierana z takiego okienka zawsze jest w formacie tekstowym (string). Powyższe równanie będzie miało więc przykładowo postać:

```js
if ("7" > 5) {
    console.log(`Liczba 7 jest większa od 5`);
}
```

co równoznaczne jest z tym, że przyrównujemy do siebie tekst z liczbą.

W Javascript możemy sprawdzać ze sobą dowolne typy danych.

W przypadku porównywania stringów działa tak zwane porównywanie leksykograficzne (słownikowo), gdzie teksty porównywane są litera po literze:

```js
console.log("ab" > "aa"); //true
console.log("pies" > "kot"); //true
console.log("abc" > "acc"); //false
console.log("alicja" > "bela"); //false
console.log("Marcin" > "Ania"); //true - znaczy lepszy 😏
```

Przy takim porównaniu stringów brane są pod uwagę pozycje liter na tablicy znaków Unicode. Powoduje to, że "a" jest większe od "A", ponieważ znajduje się na dalszej pozycji w takiej tabeli.

```js
console.log("a" > "A"); //true
console.log("Kot" > "kot"); //false

//dlatego warto przed konwersją zrównać wielkość liter
console.log("alicja" > "Beata"); //true
```

W przypadku porównywania wartości innych typów są one konwertowane do liczb, a następnie porównywane są te liczby:

```js
console.log("3" > 2); //true bo 3 > 2
console.log("02" > 3); //false bo 2 > 3
console.log("0" == 0); //true

console.log(true > 2); //false bo true to 1
console.log(false < 2); //true bo false to 0

console.log("Ala" > 0); //false bo konwersja "Ala" na liczbę to NaN (not a number), a NaN jest mniejsze od każdej liczby
console.log("Kot" > -Infinity); //false - to samo co powyżej. NaN jest mniejsze od każdej liczby
```

Tyle tylko, że to, że się da, wcale nie oznacza, że powinieneś robić takie porównania...

Polecam na co dzień pisać może i nieco dłuższy, ale i bezpieczniejszy kod. Tworząc więc warunki staraj się konwertować dane wartości do podobnego typu.

```js
//zamiast
const nr = prompt("Podaj liczbę z zakresu 1-10");
if (nr > 5) { ... }

//napiszę
const nr = Number(prompt("Podaj liczbę z zakresu 1-10"));
if (nr > 5) { ... }
```

Dzięki takiej manualnej konwersji unikniesz błędów w sytuacjach, które na pierwszy rzut oka wydają się całkowicie prawidłowe:

```js
const nr = prompt("Podaj liczbę 10");
if (nr == 10) {
    console.log(nr + nr);
}
```

W powyższym kodzie w konsoli powinniśmy dostać wynik 20, jednak rezultatem jest `1010`. Wynika to z tego, że `prompt()` (podobnie do kontrolek formularzy) zwraca nam dane w formacie tekstowym. Porównanie z linii 2 nie sprawdza typu a tylko wartość, więc `"10" == 10` jest prawdziwe. Kod się wykonuje i w rezultacie dostajemy `"10" + "10"` co daje nam wynik `"1010"`.

Stąd też polecam stosować porównywanie za pomocą trzech znaków (=== lub !==), który w teście bierze pod uwagę także typ danych. Dzięki temu kod taki zmusza nas do jawnej konwersji danej zmiennej na odpowiedni typ:

```js
const nr = prompt("Wpisz liczbę 10");
if (nr === 10) { //nic nie zobaczymy w konsoli bo "10" nie równa się 10
    console.log(nr + nr);
}
```

```js
const nr = Number(prompt("Wpisz liczbę 10"));
if (nr === 10) { //to zadziała
    console.log(nr + nr); //20
}
```

Tutaj warto wspomnieć, że Webstorm domyślnie każdorazowe użycie podwójnego porównania (==) zaznacza na żółto, co oznacza potencjalny problem. Jeżeli nie chcesz żółtych śmieci na ekranie edytora - stosuj potrójne porównanie 😏 . ...Z takim argumentem nie ma co dyskutować.

## Wartości falsy <a href="#falsy" id="falsy"></a>

Tworząc warunki, nie musimy porównywać ze sobą dwóch wartości. Wartością false staje się każda z poniższych wartości. Są to tak zwane wartości **falsy**:

```js
if (false) { ... }
if (null) { ... }
if (undefined) { ... }
if (0) { ... }
if (NaN) { ... }
if ("") { ... }
if (document.all) { ... }
```

Dla nas oznacza to tyle, że tworząc warunki możemy wykonywać kod w zależności od stanu danej zmiennej, ale też wartości wpisanej ręcznie w nawias:

```js
const a = 20;
const b = 0;
const c = null;

if (a) { //to się wykona bo a !== 0
    console.log("A ma wartość ", a);
}
if (b) { //to się nie wykona bo b === 0
    console.log("B ma wartość ", b);
}
if (c) { //to się nie wykona bo null
    console.log("C ma wartość ", c);
}
if (false) { //to się nie wykona bo false to false
}
```

Każda inna wartość daje w rezultacie true. Możemy to sprawdzić konwertując dane wartości na Boolean:

```js
Boolean(false); //false
Boolean(null); //false
Boolean(undefined); //false
Boolean(0); //false
Boolean(NaN); //false
Boolean(""); //false
Boolean(document.all); //false

Boolean("Ala"); //true
Boolean(2); //true
Boolean(2-2); //false

let x;
Boolean(x); //false bo x nie ma wartości czyli undefined
```

Powyższe sprawia, że dość często spotkasz zapisy jak poniżej:

```js
if (nr) { //kod się wykona jeżeli wartość liczby nr jest różna od falsy
}

const txt = "Ala ma kota";
if (txt.length) { //sprawdzam długość tekstu. Jeżeli większa od 0 to true
    ...
}

const tab = []
if (tab.length) { //podobnie sprawdzam długość tablicy
    ...
}
```

## Instrukcja if <a href="#if" id="if"></a>

Powyżej w listingach używałem instrukcji **if**, która wykonuje dany kod tylko w przypadku, gdy w nawiasach wynikiem będzie prawda:

```js
const nr = Math.random() * 10;

if (nr > 5) {
    console.log("Liczba nr jest większa od 5");
}
```

Dla każdej instrukcji if możemy zastosować zapisy `else` i `else if`:

```js
const nr = Math.random() * 10;

if (nr >= 5) {
    console.log("Liczba nr jest większa lub równa 5");
} else {
    console.log("Liczba nr jest mniejsza od 5");
}
```

```js
const nr = Math.random() * 10;

if (nr < 3) {
    console.log("Liczba jest mniejsza od 3");
} else if (nr <= 6) {
    console.log("Liczba jest mniejsza lub równa 6");
} else {
    console.log("Liczba jest większa od 6");
}
```

```js
const name = "Marcin";

if (name === "Marcin") {
    console.log("Marciny są fajne");
} else if (name === "Ania") {
    console.log("Anie są fajne");
} else if (name === "Radosław") {
    console.log("Radki są fajne");
} else {
    console.log("Nie wiem kto jest fajny");
}
```

Co kiedy stosować? Wszystko zależy od sytuacji. Czasami wystarczy zwykłe `if`, czasami musimy posiłkować się `else`, a czasami `else if` będzie tym czego potrzebujemy. Zwróć tylko uwagę, że przy każdej instrukcji if (w tym else if) musimy podać warunek, natomiast else zawsze jest bez warunku.

Dodatkowo takie instrukcje mogą być także zagnieżdżone:

```js
const x = 1;
const y = 2;

if (x > 0) {
    if (y > 0) {
        ...//jeżeli x > 0 i y > 0
    }
}

//to samo co powyżej
if (x > 0 && y > 0) {
    ...//kod wykonywany jeżeli liczba > 0 i druga_liczba > 0
}
```

```js
const checkName = true;
const name = "Ala";

if (checkName) {
    if (name === "Ala") {
        console.log("Imię zaczyna się na A");
    }
    if (name === "Beata") {
        console.log("Imię zaczyna się na B");
    }
    if (name === "Monika") {
        console.log("Imię zaczyna się na M");
    }
}
```

## Operator warunkowy <a href="#operator-warunkowy" id="operator-warunkowy"></a>

Operator warunkowy (tak zwany **ternary operator**), to tak naprawdę skrócona wersja warunku **if**:

```js
const x = (wyrażenie) ? jeżeli_true : jeżeli_false
```

```js
const i = 1;

let number = "";
if (i > 0) {
    number = "dodatnia";
} else {
    number = "ujemna";
}


//to samo tylko w skróconej wersji
const number = (i > 0)? "dodatnia" : "ujemna";
```

Przykłady zastosowania:

```js
const x = 23;
const isEven = (x % 2 === 0)? "parzysta" : "nieparzysta";
console.log(isEven); //"nieparzysta"

const age = 21;
const status = (age < 18) ? "jesteś za młody" : "zapraszamy na seans";
console.log(status); //"zapraszamy na seans"

const name = "Ola";
console.log( (name === "Ola") ? "Masz na imię Ola" : "Nie masz na imię Ola" ) //"Masz na imię Ola"

const nr = 10;
const answer = nr ? "yes" : "no";
console.log(answer);

const isMember = true;
console.log( `Koszt usługi to ${ (isMember ? "2.00" : "10.00") }zł` );
```

## switch <a href="#switch" id="switch"></a>

Instrukcja **switch** jest kolejnym sposobem tworzenia warunków - tym razem na zasadzie przyrównania wyniku do konkretnych wartości.

```js
const animal = prompt("Wpisz jakiego masz zwierzaka");

switch (animal) {
    case "pies":
        console.log("Psy są najlepsze");
        break;
    case "kot":
        console.log("Koty są lepsze od psów");
        break;
    case "chomik":
        console.log("Każdy chomik jest super");
        break;
    default:
        console.log("Jakiś dziwny ten zwierzak");
}
```

Każdy przypadek kończy się słowem **break**, które kończy wykonywanie instrukcji switch.\
Jeżeli pominiemy to słowo, wtedy nawet przy pomyślnym przyrównaniu zostaną wykonane kolejne sprawdzenia, co często może powodować błędy.\
Dodatkowo instrukcja switch ma specjalny przypadek **default** który będzie wybierany, gdy wszystkie inne przypadki będą błędne (odpowiednik else w instrukcji if).

Warto zaznaczyć, że instrukcja switch nie służy do testowania warunków, a do przyrównywania zmiennej do wartości:

```js
const car = "bmw";

switch (car) {
    case "bmw" : ... break;
    case "fiat" : ... break;
    case "audi" : ... break;
}

//poniższe przyrównywanie nie zadziała
const nr = 5;
switch (nr) {
    case (nr <= 5):
        console.log("Mało");
    break;
    case (nr > 5 && nr <= 10):
        console.log("Średnio");
    break;
    case (nr > 15) :
        console.log("Dużo");
    break;
}
```

Jednak i tutaj można przyrównać do warunków. Wystarczy przyrównywać zamiast do zmiennej to do wartości true:

```js
const nr = 5;

switch (true) {
    case (nr <= 5):
        console.log("Mało");
    break;
    case (nr > 5 && nr < 10):
        console.log("Średnio");
    break;
    case (nr >= 10) :
        console.log("Dużo");
    break;
}
```

Zastosowanie tego jest niespotykane i traktował bym to jako ciekawostkę.
