# Zmienne i stałe

[Zmienne](https://pl.wikipedia.org/wiki/Zmienna_\(informatyka\)) to coś w rodzaju "pudełek", w których pod nazwami możemy przechowywać różne rzeczy takie jak liczby, teksty, obiekty itp.

## Deklarowanie zmiennych <a href="#deklarowanie-zmiennych" id="deklarowanie-zmiennych"></a>

Aby zadeklarować zmienną, powinniśmy posłużyć się jednym ze słów kluczowych.\
Do stworzenia zmiennej w JavaScript przez lata używało się słowa kluczowego **var**:

```js
var txt = "Ala ma kota";
var nr = 20;
var url = "kontakt.html";

console.log(txt);
console.log(nr);
console.log(url);
```

JavaScript ewoluuje. W 2015 roku pojawiła się ECMAScript 2015 wprowadzając sposoby deklaracji zmiennych za pomocą słów **let** i **const**. Słowo **let** oznacza zmienną, natomiast **const** stałą:

```js
let txt = "Przykładowy tekst"; //zmienna
txt = "Inny tekst"; //zmieniamy wartość

const nr = 102; //stała
nr = 103; //błąd - nie można przypisać nowej wartości
```

W dzisiejszych czasach mimo tego, że w internecie spotkamy miliony skryptów wykorzystujących `var`, zaleca się stosować **const** i **let**. Dzięki nim nasze skrypty nie tylko działają lepiej pod względem zarządzania pamięcią, ale i potencjalnie unikamy niektórych problematycznych sytuacji, które występowały przy var (a o których dowiesz się w dalszej części kursu).

Przeglądając Internet zapewne natrafisz na wiele przykładów skryptów, gdzie autorzy tworzą zmienne z pominięciem słów kluczowych var/let/const np:

```js
nr = 200;
console.log(nr); //200

for (i=0; i<100; i++) {
    ...
}

for (el of array) {
    ...
}
```

Nie rób tak. Tworząc zmienne nie pomijaj słów let/const/var. W kolejnych rozdziałach dowiesz się czemu.

## Po co stosować zmienne? <a href="#po-co-stosowac" id="po-co-stosowac"></a>

Po co w ogóle stosować zmienne? Rozpatrzmy prosty przykład dodawania liczb:

```js
console.log(6 + 5 + 1);
console.log(4 + 5 + 2);
console.log(1 * 5 + 5 - 2);
console.log(8 + (5 * 10) + 5 + 9 + (5 * 10) + 5);
...
...
```

Przypuśćmy, że teraz chcielibyśmy zmienić cyfrę 5 w każdym równaniu.

Dla tak małego skryptu zmiana tej cyfry nie jest wielkim problemem. Co by się jednak stało gdybyśmy takich równań w naszym przykładzie mieli na przykład tysiąc lub nawet sto tysięcy?\
I tu właśnie przychodzą nam z pomocą zmienne:

```js
const nr = 4;

console.log(6 + nr + 1);
console.log(4 + nr + 2);
console.log(1 * nr + nr - 2);
console.log(7 + (nr * 10) + nr + 9 + (nr * 10) + nr);
```

Pod zmienne możemy podstawiać nie tylko liczby, ale teksty, funkcje, obiekty, tablice - czyli dowolne rzeczy, na których będziemy pracować w naszych kodach:

```js
const min = 5;
const max = 15;

//dzięki zmiennym poprawia mi się czytelność kodu i łatwiej go zmieniać
const random = Math.floor(Math.random()*(max-min+1)+min);
console.log(random);
```

```js
//pod zmienną wstawiam pobrany ze strony element
const btn = document.querySelector(".button");

//i zaczynam na niej działać
btn.classList.add("btn-primary");
btn.setAttribute("title", "Kliknij mnie");
btn.innerText = "Kliknij mnie!"
```

## Nazewnictwo zmiennych <a href="#nazewnictwo-zmiennych" id="nazewnictwo-zmiennych"></a>

Nazwy zmiennych i stałych które deklarujemy nie mogą być byle jakie. Istnieją pewne zasady których musimy się trzymać. I tak:

* wielkość liter ma znaczenie. Zmienna myTXT to nie to samo co mytxt
* nazwa zmiennej nie może zaczynać się od cyfry,
* nazwa zmiennej nie może zawierać spacji, kropki, przecinka ani myślnika (można natomiast używać podkreślenia),
* nazwą zmiennej nie może być [słowo kluczowe](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords) zarezerwowane przez JavaScript

**Po prostu nie zaczynaj nazw od cyfr i pisz nazwy zmiennych po angielsku.** Bardzo częstą praktyką jest stosowanie zapisu camelCase, czyli np. `veryImportantThing`, ale wiele osób stosuje też zapis z podłogą czyli `very_important_thing`.

Jest jednak ważniejsza sprawa, którą zapamiętaj.\
**Nazywaj swoje zmienne tak, by dało się zrozumieć do czego się odnoszą**. Zmienna o nazwie **elementsCount** jest bardziej czytelna, niż **xxx**. W tym kursie czasami będę korzystał z mało czytelnych zmiennych (np. a), ale wynika to tylko z tego, że kod często jest bardzo, bardzo krótki, a i jestem leniem.

Dla ciekawskich - nazwy zmiennych mogą być [bardzo różne](https://mathiasbynens.be/notes/javascript-identifiers). Nie jest to oczywiście zalecane i trzeba to traktować jako ciekawostkę.

```js
//dobre nazwy
const myAge = 10;
const my_age = 10;

//błędne nazwy
const my-age = 10;
const 2myAge = 10;
const my age  = 10;
const mójWiek = 10;
```

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

Jedną z głównych bolączek `var` od zawsze było to, że za pomocą tego słowa można tworzyć tylko zmienne. Oznacza to, że w każdym momencie mogę później taką zmienną zmienić:

```js
var key = "21389123718398";

...

key = 20;
```

W celu uniknięcia błędów część programistów nazywało stałe dużymi literami. Dzięki temu osoba pisząca kod wiedziała czy powinna czy nie powinna zmieniać danej zmiennej.

```js
var KEY = "21389123718398";
```

...konwencja ta jest nadal przez wielu stosowana - co jest akurat bardzo spoko, ponieważ po samej nazwie zmiennej nie sposób wykryć czy można zmienić jej wartość.

Wraz z wprowadzeniem nowych słów let/const dostaliśmy rozróżnienie na zmienne i stałe.\
Zmienne deklarowane za pomocą **let/var** można w przyszłości zmienić - czyli podstawić im nową wartość. Zmiennym stworzonym za pomocą **const** nie jesteśmy w stanie podstawić nowej wartości.

```js
var text = "ala";
text = "ala ma kota"; // wszystko ok, bo var to zmienna

let a = 0;
a = 10; //wszystko ok, bo let

const b = 0;
b = 10; //błąd - do stałej nie możemy przypisać nowej wartości

const name = "Ala";
name = "Monika"; //błąd
```

Jeżeli jednak pod daną stałą const podstawimy jakiś obiekt, bez problemu będziemy mogli zmieniać jego właściwości:

```js
const tab = [1,2,3]; //tablica to też obiekt
tab[3] = 4;
tab.push(5, 6);
console.log(tab); //[1, 2, 3, 4, 5, 6]
tab = [1, 2, 3, 4, 5, 6]; //błąd - podstawiłem zupełnie nową tablicę

const user = {
    name: "Piotr",
    age: 18
}
user.age = 20;
user = { //błąd - bo podstawiłem totalnie nowy obiekt
    name: "Piotr"
}
```

### Różnice między var a let/const <a href="#zasieg-zmiennych" id="zasieg-zmiennych"></a>

Deklaracje let/const nie tylko wprowadziły stałe, ale także kilka różnic w stosunku do var.

Pierwsza i najważniejsza różnica między let/const a var to zasięg zmiennych.

W przypadku let/const zmienne mają zasięg [blokowy](https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Polecenia/block), co w skrócie oznacza "od klamry do klamry":

```js
let a = 20; //zmienna globalna

{
    let a = 30; //zmienna lokalna
    console.log(a); //30
}

console.log(a);    //20
```

```js
{
    let a = "Ala";
    console.log(a); //Ala
}
{
    console.log(a); //error - nie ma takiej zmiennej
}
{
    let a = "Ola"; //zmienna lokalna w tym bloku
    console.log(a); //Ola
}
console.log(a); //error: a is not defined
```

```js
for (let i=0; i<10; i++) {
    console.log(i);
}

console.log(i); //error: i is not defined
```

```js
{
    let nr = 102;
    console.log(nr); //102
}
{
    console.log(nr); //błąd: nr is not defined
}
console.log(nr); //błąd: nr is not defined
```

Zmienne deklarowane za pomocą **var** mają natomiast zasięg funkcyjny, czyli ich zasięg określa ciało funkcji.

```js
var a = 20; //zmienna globalna

function test() {
    var a = 30; //zmienna lokalna
    console.log(a); //30
}
test();

console.log(a);    //20
```

W nielicznych sytuacjach może to powodować niezamierzone działanie kodu.

```js
if (true) {
    var myVar = 20;
}

console.log(myVar); //20
```

```js
for (var i=0; i<10; i++) {
    console.log(i);
}

console.log(i); //10
```

W kolejnych rozdziałach natrafisz na przykłady bardziej praktyczne...

Kolejna cecha rozróżniająca var od let/const jest taka, że zmienne var możemy ponownie deklarować, co jest niemożliwe w przypadku let i const:

```js
var name = "Marcin";
var name = "Karol";
console.log(name); //Karol
```

```js
let name = "Marcin";
let name = "Karol"; //błąd = Identifier "name" has already been declared
console.log(name);
```

W przypadku const/let musimy stosować inne bloki:

```js
{
    let name = "Marcin";
    console.log(name);
}
{
    let name = "Karol";
    console.log(name);
}
```

Kolejna różnica między starszą deklaracją var a jej młodszymi braćmi to tak zwany **hoisting** (windowanie).\
JavaScript lubi pomagać programiście. Jednym z takich przypadków pomocy jest niewidoczne dla programisty wynoszenie deklaracji "na początek kodu" (w pierwszym momencie twój kod czytany jest przez silnik Javascript (jest parsowany), a następnie tworzony jest specjalny "kontekst wykonawczy", czyli miejsce w pamięci, gdzie zawarte są wszystkie zmienne i funkcje używane przez ciebie - bardzo dobrze opisuje to film <https://www.youtube.com/watch?v=Fd9VaW0M7K4>).

W przypadku var odwołanie się do zmiennej przed jej stworzeniem nie rzuci nam błędem, natomiast pokaże `undefined`:

```js
var a; //niewidocznie przenoszona jest sama deklaracja - bez wartości
console.log(a); //undefined
var a = 200;
```

Deklaracja zmiennej bez wartości wynoszona jest automatycznie na początek danego kodu (a w zasadzie na początek danego zakresu - np. na początek danej funkcji), w wyniku czego nasz powyższy skrypt w momencie wykonywania ma postać:

```js
var a; //js przeniósł tutaj deklarację zmiennej ale bez jej wartości!
console.log(a); //wypisze undefined, ale błędu nie ma

var a = 20;
```

Niektórzy programiści - szczególnie ci, którzy na co dzień używali innych języków, w których takie zjawisko nie występuje - dla uniknięcia takich niejawnych (a i niewidocznych dla nas) zachowań - stosowali konwencję deklaracji każdej zmiennej na początku kontekstu:

```js
var text, age; //jeżeli nie podstawiamy wartości to równe są undefined

age = 20;
text = "Ala ma " + age + " lat";
```

W przypadku `let`/`const` (ale też class) hoisting także istnieje, ale nie jesteśmy w stanie używać zmiennych przed ich zadeklarowaniem:

```js
console.log(a); //Error: Cannot access "a" before initialization

let a = 20;
```

To samo będzie się tyczyło wyrażeń funkcyjnych, czyli sytuacji, gdzie funkcję podstawiamy pod zmienną. Znowu - nie możemy użyć zmiennej przed jej stworzeniem:

```js
myFn(); //Error: Cannot access "myFn" before initialization

const myFn = function() {
    console.log("Lubie koty i psy");
}
```

Ale o tym porozmawiamy sobie w rozdziale o funkcjach.

Miejsce przed deklaracją zmiennej let/const zwie się **temporal dead zone**, bo nie możemy odwoływać się do zmiennej, której jeszcze nie zadeklarowaliśmy. Dzięki takiemu zabiegowi nasz kod staje się bardziej logiczny - najpierw tworzymy wszystkie klocki (zmienne, funkcje), a dopiero potem ich używamy.

Ostatnią różnicą - dość mało znaną - jest to, że deklarując zmienną globalną var (poza ciałem funkcji), dodawana jest ona jako właściwość obiektu window. W przypadku let nic takiego się nie dzieje:

```js
var a = 20;
let b = 30;

console.log(window.a); //20
console.log(window.b); //undefined
```

Gdy wypiszemy sobie w konsoli obiekt window, pokaże nam się masa różnych rzeczy, które możemy (i zapewne będziemy) używać:

```js
console.log(window);
```

<figure><img src="https://kursjs.pl/kurs/super-podstawy/window-nieumyslne-nadpisanie1.png" alt=""><figcaption></figcaption></figure>

Tworząc w powyższy sposób globalne zmienne var, moglibyśmy przez przypadek nadpisać niektóre funkcjonalności, które w przyszłości chcielibyśmy użyć:

```js
var alert = "Tekst informacji";
var fetch = "Pobieram dane";
```

<figure><img src="https://kursjs.pl/kurs/super-podstawy/window-nieumyslne-nadpisanie2.png" alt=""><figcaption></figcaption></figure>

```js
alert("Wypełnij te pola"); //błąd
fetch("https://jsonplaceholder.typicode.com/") //błąd
```

Przy czym nie powinienem tutaj demonizować. Jeżeli piszesz swoje skrypty cokolwiek ponad 0 poziom wtajemniczenia, to raczej nie powinieneś nigdy natrafić na takie błędy. Ale któż wie...

To jak jesteśmy przy głupotach JS (o czym można by niezły art napisać), to jedną z kolejnych rzeczy jest fakt, że dla każdego elementu z ID tworzona jest zmienna o takiej samej nazwie, która wskazuje na dany element:

```js
//nie stworzyłem nigdzie takiej zmiennej ale mam do niej dostęp
//bo na tej stronie jest element #mainContent (główna treść)

console.log(mainContent);
```

W odczuciu autora tego kursu jest to złe, ponieważ dostajemy zmienne, które powstają z nieba. Jeżeli będziemy pracować na elementach z ID, mimo że JavaScript stworzył dla nich zmienne, mimo wszystko lepszym wydaje się je jawnie pobrać za pomocą odpowiednich funkcji. Ale to już w rozdziale o DOM

## Podsumowanie <a href="#podsumowanie" id="podsumowanie"></a>

Ok podsumujmy powyższe rozważania.

* Zmienne to rodzaje pudełek, w których możemy trzymać różne rzeczy.
* Zmienne możemy tworzyć za pomocą słów kluczowych var/let/const, przy czym zalecane są te dwa ostatnie
* Let/const różnią się od varów głównie zasięgiem oraz tym, że w jednym zasięgu (bloku) nie możemy ponownie tworzyć zmiennych o tej samej nazwie.
* Hoisting to zjawisko wynoszenia na początek skryptu zmiennych i deklaracji funkcji
* W naszych skryptach starajmy się używać jak najwięcej const - dzięki temu będziesz wyglądał jak pro. Jedynym wyjątkiem są liczniki oraz zmienne które wiemy, że zaraz zmienimy (np. toggleCatNightPartyMode)

I w zasadzie tyle. Cała reszta przyjdzie z praktyką. Nawet jeżeli na chwilę obecną powyższe rozważania wydają się dla ciebie dziwne, nie przejmuj się. Po 2-3 skryptach całość stanie się odruchem.
