Przewodnik po zmiennych w JavaScript – część 1
Zmienne to jedno z najbardziej podstawowych pojęć w programowaniu. Na wysokim poziomie są one nazwami, do których możesz przypisać wartości, a następnie uzyskać do nich dostęp i używać ich do wykonywania operacji. Ale to duże uogólnienie, bo faktycznie do zrozumienia jest o wiele więcej, a JavaScript ma pewne niuanse, które tego nie ułatwiają.
W JavaScript istnieje kilka sposobów deklarowania zmiennych. Zmienne mogą zachowywać się różnie w zależności od tego, jaka wartość jest do nich przypisana. Zmienne mają zakres, który określa, gdzie i kiedy można uzyskać do nich dostęp.
W pierwszej części tego artykułu opowiemy czym naprawdę są zmienne i jak najlepiej myśleć o zmiennych. W części drugiej zajmiemy się sposobami deklarowania zmiennych, oraz zakresem zmiennych w JavaScript.
Czytaj dalej, aby dowiedzieć się wszystkiego, co musisz wiedzieć o zmiennych.
Spis treści
Jak rozumieć zmienną?
Dla przedstawienia koncepcji zmiennych często używana jest metafora pudełka o określonej nazwie. Tworzymy pudełko, nadajemy mu jakąś nazwę, na przykład „foo”, i wkładamy do niego wartość, na przykład słowo „kot”:

Następnie możemy sięgnąć po to pudełko, by odzyskać jego zawartość. Możemy też włożyć do niego coś innego. Ale, co ważne, w dowolnym momencie w pudełku może znajdować się tylko jedna wartość:

W kodzie JavaScript mogłoby to wyglądać tak:
// tworzymy pudełko i umieszczamy w nim początkową wartość
var foo = "kot";
// sięgamy po pudełko by wyjąć jego zawartość
console.log(foo);
// wkładamy coś innego
foo = 7;
// i znów odzyskujemy zawartość
console.log(foo);
Na tym poziomie wygląda to naprawdę jasno i prosto. Ale metafora pudełka załamie się, gdy spróbujemy przechować w zmiennej wartość, która nie jest typem prostym (typy proste i złożone to temat na osobny artykuł, na ten moment wystarczy, że wiesz, iż string
, number
i boolean
są typami prostymi, a tablice i obiekty typami złożonymi.)
Spójrz na taki przykład:
// tworzymy zmienna i przypisujemy do niej tablicę z 3 elementami
var foo = [10, 20, 30];
// tworzymy nową zmienną i przypisujemy do niej wartość zmiennej foo
var bar = foo;
// zmieniamy wartość pierwszego elementu tablicy przypisanej do foo
foo[0] = 99;
// i teraz sprawdzamy wartość zmiennej bar
console.log(bar);
Jak myślisz, jaki rezultat da ostatni console.log?
Będzie to:
[99, 20, 30];
Jak widać, gdy zmieniliśmy wartość elementu tablicy foo
, zmienił się on również w tablicy bar
. Dlaczego tak się dzieje? Pozostając przy wyobrażeniu pudełka, czy ta sama tablica znajduje się jednocześnie w obydwu pudełkach? A może zmienna bar
zawiera zmienną foo
? Żadna z tych rzeczy nie jest prawdą, ale zanim wyjaśnię o co tak naprawdę tutaj chodzi, chciałbym zaproponować Ci inny sposób myślenia o zmiennych. Zamiast metafory pudełka, proponuję metaforę etykiety.
Działa ona tak. Na żółtej karteczce tworzymy etykietę foo
. Najpierw przyklejamy ją do słowa „kot”, by następnie ją odczepić i przykleić do liczby 7
.

Spróbujmy teraz zastosować tę metaforę do przykładu z tablicą:
- Tworzymy etykietę
foo
i przypinamy ją do tablicy z 3 elementami. - Następnie tworzymy nową etykietę
bar
, i przypinamy ją do tego, do czego przypięta jest etykietafoo
, czyli do tej samej tablicy. - Gdy teraz zmienimy wartość któregoś z elementów tablicy, to ta zmieniona wartość będzie widoczna zarówno gdy sięgniemy do tablicy za pomocą etykiety
foo
, jak i poprzez etykietębar
.
![3 klatki. W pierwszej klatce etykieta foo jest przyczepiona do tablicy [10, 20, 30]. W drugiej klatce do tej samej tablicy dodana jest druga etykieta: bar. W trzeciej klatce zmieniony został pierwszy element tablicy na liczbę 77. Obie zmienne foo i bar odzwierciedlają tę zmianę w momencie wywołania console.log()](https://devschool-pl.preview-domain.com/wp-content/uploads/2024/12/pic-4-477x1024.png)
Tak jak pudełko nie mogło zawierać dwóch wartości jednocześnie, tak samo i etykieta nie może być przypięta jednocześnie do dwóch różnych rzeczy. Natomiast dwie lub więcej etykiet może być przypiętych do tej samej rzeczy (mam na na myśli dosłownie tę samą rzecz, a nie wyglądjącą tak samo kopię). W tym sensie, metafora etykiety tworzy o wiele lepszy model mentalny tego, jak naprawdę działają zmienne.
Tak jak pudełko nie mogło zawierać dwóch wartości jednocześnie, tak samo i etykieta nie może być przypięta jednocześnie do dwóch różnych rzeczy. Natomiast dwie lub więcej etykiet może być przypiętych do tej samej rzeczy (mam na na myśli dosłownie tę samą rzecz, a nie wyglądjącą tak samo kopię). W tym sensie, metafora etykiety tworzy o wiele lepszy model mentalny tego, jak naprawdę działają zmienne.
Czym naprawdę jest zmienna?
Komputer przechowuje dane w komórkach pamięci RAM. Komórki te mają numeryczne adresy, dzięki którym procesor jest w stanie do nich sięgnąć i odczytać lub zapisać informację.

JavaScript jest językiem wysokiego poziomu, więc programując w nim nie musimy zajmować się bezpośrednio operacjami na pamięci RAM, wykonuje je dla nas silnik JavaScript.
Silnik JavaScript podczas wykonywania programu korzysta z dwóch obszarów pamięci: stosu (stack) oraz sterty (heap. Stos jest mniejszy i uporządkowany, JS przechowuje w nim środowiska aktualnie wykonywanych funkcji (execution contexts). Wartości proste (primitive values) przechowywane są bezpośrednio w stosie. Sterta jest znacznie większa, nieuporządkowana, i służy do przechowywania wszystkich tych danych, które nie są trzymane w stosie. To tutaj znajdują się dane typów złożonych: tablic i obiektów.
Każda dana, zarówno w stosie jak i w stercie, zajmuje pewną liczbę komórek pamięci, począwszy od określonego adresu. Zmienne pozwalają nam korzystać z tych danych. W jaki sposób? W tym miejscu pomoże nam metafora etykiety, bo w rzeczywistości zmienna jest nazwą wskazującą na miejsce w pamięci. Zmienna przechowuje nie samą wartość, ale adres pamięci, w którym dana wartość się znajduje.

Czyli przypisując do zmiennej nową wartość, faktycznie zmieniamy informację o wskazywanym przez nią adresie pamięci RAM. Przepinamy etykietę.
W momencie przypisania do zmiennej nowej wartości, dla typów prostych ta wartość zastępuje starą (stara zostaje zapomniana), a w przypadku typów złożonych zmieniany jest tylko sam wskaźnik (referencja) do adresu w stercie.
Dlatego możemy mieć dwie zmienne wskazujące na dosłownie tę samą tablicę. Użyta we wcześniejszym przykładzie 3-elementowa tablica jest przechowywana w stercie, a zmienne foo
i bar
zawierają adres pamięci RAM, w którym dane owej tablicy mają swój początek.
Ponadto, JavaScript jest językiem o dynamicznym typowaniu, więc ta sama zmienna może w jednym momencie wskazywać na wartość prostą przechowywaną w stosie, a chwilę później na wartość złożoną zlokalizowaną w stercie.
Działania na zmiennych
Napiszę jeszcze krótko o działaniach na zmiennych. Krótko, bo… w rzeczywistości nie działamy na zmiennych! Faktycznie za pośrednictwem zmiennych wykonujemy działania na wartościach, na które te zmienne wskazują.
Czyli:
var str1 = "Java",
str2 = "Script";
console.log(str1 + str2);
jest po prostu tym samym co:
console.log("Java" + "Script");
// rezultat: JavaScript
Dlatego nie będziemy teraz rozważać jakie mogą być rezultaty różnych działań (np. 2 + "3"
) i dlaczego tak się dzieje. Ten temat nie ma związku ze zmiennymi jako takimi i zasługuje na osobny artykuł.
Podsumowanie
Najważniejsze informacje do zapamiętania z tej części artykułu:
- Zmienne są jak etykiety przypięte do danych przechowywanych w pamięci RAM.
- Zmienne w JavaScript zachowują się inaczej gdy przypisujemy do nich wartości proste, a inaczej gdy wartości złożone.
- Dla wartości prostych każda zmienna wskazuje na indywidualną kopię danej wartości przechowywaną w stosie.
- Dla wartości złożonych zmienne zawierają referencje do obiektów położonych w stercie.
W drugiej części przewodnika po zmiennych zajmiemy się zasięgiem zmiennych oraz różnymi sposobami deklarowania zmiennych. Omówimy też łańcuch zasięgów oraz przesłanianie zmiennych: