Gdy kod JavaScript zaczyna rosnąć, słowo kluczowe var potrafi zaskoczyć bardziej niż sama składnia. Wyjaśnię, jak działa deklaracja zmiennych z użyciem var, czym różni się od let i const, a także skąd biorą się problemy z zakresem, ponowną deklaracją i hoistingiem.
Najważniejsze zasady używania var w JavaScripcie
-
varma zakres funkcji, a nie pojedynczego bloku kodu. - Możesz ponownie zadeklarować zmienną i przypisać jej nową wartość.
- Deklaracje
varpodlegają hoistingowi, więc przed przypisaniem wartości otrzymująundefined. - W nowym kodzie najczęściej lepiej wybrać
constlublet. -
varnadal pojawia się w starszych projektach, bibliotekach i kodzie wymagającym zgodności wstecznej.
Do czego służy var w JavaScripcie
var służy do deklarowania zmiennej, czyli nazwania miejsca, w którym program przechowuje wartość. Możesz od razu nadać jej wartość albo zrobić to później.
var imie = "Alicja";
var liczbaPunktow = 120;
console.log(imie);
console.log(liczbaPunktow);
W pierwszym przykładzie zmienna została jednocześnie zadeklarowana i zainicjalizowana. W drugim przypadku można rozdzielić te operacje.
var komunikat;
komunikat = "Witaj w aplikacji";
console.log(komunikat);
Zmienna utworzona za pomocą var może później otrzymać inną wartość. To odróżnia ją od const, której nie można ponownie przypisać.
var status = "oczekuje";
status = "zakończono";
console.log(status);
Sam zapis jest prosty, ale największe znaczenie ma zakres zmiennej. Właśnie tutaj zaczynają się różnice, które w większym projekcie mogą prowadzić do trudnych do znalezienia błędów.
Zakres funkcji zmienia sposób działania var
var nie respektuje zakresu blokowego. Oznacza to, że zmienna utworzona wewnątrz if, pętli albo zwykłych nawiasów klamrowych nadal jest dostępna w całej funkcji.
function sprawdzWynik() {
if (true) {
var wynik = 10;
}
console.log(wynik);
}
sprawdzWynik(); // 10
Dla osoby przyzwyczajonej do let lub const może to być nieintuicyjne. Współczesne deklaracje blokowe zatrzymują zmienną wewnątrz nawiasów klamrowych, natomiast var wychodzi poza ten blok, o ile pozostaje w tej samej funkcji.
if (true) {
let nowaZmienna = "tekst";
const stala = 42;
var starszaZmienna = "widoczna";
}
console.log(starszaZmienna); // "widoczna"
console.log(nowaZmienna); // ReferenceError
console.log(stala); // ReferenceError
To jeden z powodów, dla których w nowych projektach częściej wybieram zakres możliwie ograniczony. Im mniej kodu ma dostęp do zmiennej, tym mniejsze ryzyko przypadkowej zmiany jej wartości.
Przeczytaj również: JS indexOf - Jak znaleźć indeks w tablicy i uniknąć pułapek?
Zakres wewnątrz funkcji
Jeżeli var pojawia się wewnątrz funkcji, zmienna jest lokalna dla tej funkcji. Kod znajdujący się poza nią nie może się do niej odwołać.
function utworzUzytkownika() {
var rola = "administrator";
return rola;
}
console.log(utworzUzytkownika()); // administrator
console.log(rola); // ReferenceError
Poza funkcją deklaracja var działa jako deklaracja globalna w klasycznym skrypcie przeglądarkowym. W takim przypadku może stać się właściwością obiektu globalnego, na przykład window. Moduły ES działają inaczej, dlatego top-levelowe var w module nie tworzy właściwości window.
Hoisting sprawia, że var zachowuje się nietypowo
Hoisting, czyli wyniesienie deklaracji, oznacza, że środowisko JavaScriptu przygotowuje deklarację var przed wykonaniem kodu funkcji. Nie przenosi jednak przypisanej wartości.
console.log(nazwa); // undefined
var nazwa = "Maria";
Silnik interpretuje ten fragment mniej więcej tak:
var nazwa;
console.log(nazwa); // undefined
nazwa = "Maria";
To ważne rozróżnienie. Hoisting nie sprawia, że wartość jest dostępna wcześniej. Wcześniej istnieje tylko sama zmienna, której wartość wynosi undefined.
Inaczej zachowują się let i const. Ich deklaracje również są przygotowywane, ale przed wykonaniem linii deklaracji znajdują się w tak zwanej temporal dead zone. Próba użycia zmiennej kończy się błędem ReferenceError.
console.log(nazwa); // ReferenceError
let nazwa = "Maria";
W praktyce nie traktuję hoistingu jako funkcji, z której warto korzystać. Lepiej deklarować zmienne przed ich użyciem, bo kod staje się czytelniejszy i nie wymaga od odbiorcy odtwarzania kolejności działania silnika.
Var, let i const w codziennym kodzie
Trzy słowa kluczowe służą do deklarowania zmiennych, ale różnią się zakresem, możliwością ponownego przypisania i zachowaniem przed deklaracją.
| Słowo kluczowe | Zakres | Ponowne przypisanie | Ponowna deklaracja w tym samym zakresie |
|---|---|---|---|
var |
Funkcja lub globalny | Tak | Tak |
let |
Blok | Tak | Nie |
const |
Blok | Nie | Nie |
W nowoczesnym JavaScripcie najczęściej zaczynam od const. Gdy wiem, że wartość będzie się zmieniać, wybieram let. var zostawiam głównie dla starszego kodu, konkretnych wymagań projektu albo sytuacji, w których trzeba zachować zgodność z istniejącą bazą kodu.
const podatek = 0.23;
let licznik = 0;
licznik += 1;
Trzeba też pamiętać, że const blokuje ponowne przypisanie zmiennej, ale nie zamraża obiektu.
const uzytkownik = {
imie: "Olek"
};
uzytkownik.imie = "Marek"; // działa
// uzytkownik = {}; // TypeError
To częste źródło nieporozumień. Stała wskazuje na ten sam obiekt, ale właściwości tego obiektu mogą być modyfikowane, jeśli nie został dodatkowo zamrożony.
Typowe błędy związane z deklaracją var
Najbardziej podstępne problemy pojawiają się wtedy, gdy var jest używane w pętli, zwłaszcza razem z funkcjami zwrotnymi. Wszystkie funkcje mogą wtedy odwoływać się do tej samej zmiennej funkcyjnej.
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
Wynik to zazwyczaj trzy razy 3, ponieważ funkcje wykonują się po zakończeniu pętli, gdy i ma już wartość końcową. Zastosowanie let tworzy osobną wartość dla każdej iteracji.
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
Drugi problem to przypadkowa ponowna deklaracja. JavaScript nie zgłosi błędu przy takim kodzie:
var wynik = 10;
var wynik = 20;
console.log(wynik); // 20
W małym przykładzie łatwo to zauważyć. W większej aplikacji druga deklaracja może ukryć literówkę albo nadpisać wartość, którą inna część funkcji nadal uważa za aktualną. let i const wykrywają taki problem wcześniej, bo ponowna deklaracja w tym samym zakresie kończy się błędem składni.
Nie należy też mylić deklaracji z przypisaniem do niezadeklarowanej zmiennej. Taki zapis jest błędem w trybie ścisłym i może tworzyć niekontrolowane zmienne globalne w starszym kodzie.
"use strict";
liczba = 10; // ReferenceError
Kiedy używać var, a kiedy zastąpić je let lub const
Jeżeli tworzysz nowy skrypt, aplikację frontendową albo moduł JavaScriptu, rozsądnym punktem wyjścia jest const jako domyślna deklaracja. Zmień ją na let dopiero wtedy, gdy wartość rzeczywiście musi być ponownie przypisywana.
- Wybierz
const, gdy zmienna nie będzie wskazywać na inną wartość. - Wybierz
let, gdy wartość będzie się zmieniać, na przykład w liczniku lub stanie formularza. - Użyj
var, gdy pracujesz ze starszym kodem i zmiana mogłaby naruszyć jego działanie. - Przy modernizacji kodu sprawdź przede wszystkim pętle, funkcje zagnieżdżone i zmienne globalne.
Samo mechaniczne zastąpienie każdego var przez let nie zawsze jest bezpieczne. Trzeba sprawdzić, czy kod korzysta z zakresu funkcji, ponownej deklaracji albo hoistingu. W przeciwnym razie można zamienić działający, choć stary fragment na kod z błędem zakresu.
Moja praktyczna zasada jest prosta: ograniczaj zasięg zmiennej i deklaruj ją możliwie blisko miejsca użycia. Dzięki temu kod łatwiej testować, refaktoryzować i przekazywać innym osobom.
Co zapamiętać przed użyciem var w nowym projekcie
var nie jest błędem samym w sobie. To starsza, nadal poprawna część języka, która ma swoje zastosowania, ale jej zakres funkcji, hoisting i możliwość ponownej deklaracji zwiększają ryzyko pomyłek.
W nowym kodzie zwykle wybierz const, później let, a var stosuj świadomie. Jeśli rozumiesz zakres funkcji, działanie hoistingu i różnicę między przypisaniem a deklaracją, bez problemu odczytasz zarówno współczesny JavaScript, jak i starsze skrypty spotykane w projektach webowych.