Zmienne i stałe

Zmienne 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

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:


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

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

JavaScript ewoluuje. Wraz z pojawieniem się ES6 (ECMAScript 2015) wprowadzono nowe sposoby deklaracji zmiennych za pomocą słów kluczowych let i const. Słowo let oznacza zmienną, natomiast const stałą (taką, do której po ustawieniu wartości, nie możemy przypisać nowej):


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


const nr = 102; //stała
nr = 103; //błąd - nie można zmieniać stałej

W dzisiejszych czasach mimo tego, że w internecie spotkamy miliony skryptów wykorzystujących var, wielu programistów zaleca stosować już const i let. Dzięki nim nasze skrypty stają się nie tylko bardziej optymalne 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). Przy czym demonizowanie var też nie jest dobre...

Ja w tym kursie będę starał się trzymać nowego zapisu.

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:


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

Jest to błąd, i nigdy tak nie rób. W kolejnych rozdziałach dowiesz się czemu ( 1, 2, 3, 4 ).

Po co stosować zmienne?

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


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:


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 praktycznie dowolne rzeczy, na których będziemy pracować w naszych kodach:


const btn = document.querySelector(".button"); //pod zmienną wstawiam pobrany button

btn.classList.add("btn-primary");
btn.setAttribute("title", "Kliknij mnie");
btn.innerText = "Kliknij mnie!"

Nie bój się tworzyć zmiennych.

Spójrz na poniższy przykład generujący losową liczbę:


const random = Math.floor(Math.random() * (max-min+1) + min);
console.log(random);

Widzisz te min i max? To zmienne. Zamiast je ręcznie zamieniać na odpowiednie liczby, o wiele lepiej stworzyć po prostu dodatkowe zmienne.


const min = 1;
const max = 15;
const random = Math.floor(Math.random()*(max-min+1)+min);
console.log(random);

Nazewnictwo zmiennych

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 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. Bardzo, bardzo różne. Nie jest to oczywiście zalecane i trzeba to traktować jako ciekawostkę.


//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

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ć:


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.


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.


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ą zmienną podstawimy jakiś obiekt, bez problemu będziemy mogli zmieniać jego właściwości:


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

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, co w skrócie oznacza "od klamry do klamry":


let a = 20; //zmienna globalna

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

console.log(a);    //20

{
    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

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

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

{
    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.


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.


if (true) {
    var myVar = 20;
}

console.log(myVar); //20

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:


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

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:


{
    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. I tak na początek kodu wynoszone są deklaracje takie jak var / let/ const / function / class. Różnica jest w sposobie takiego wynoszenia.

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


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ć:


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:


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:


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:


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:


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ć:


    console.log(window);
Nieumyślne nadpisanie

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ć:


var alert = "Tekst informacji";
var fetch = "Pobieram dane";
Nieumyślne nadpisanie

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:


//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

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.

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.