Var Let Const

Cover Image for Var Let Const
Paweł
Paweł

Lecimy z rekrutacyjnym klasykiem. Zagadnienie, które dość często pojawia się w rozmowach na stanowisko juniorskie (ale nie tylko), czyli var vs. let vs. const.

Kiedyś to było…

Programista JS’a korzystający ze standardu ES5 nie musiał się zastanawiać, jakiego rodzaju powinna być zmienna. Można je było zadeklarować tylko przy pomocy słowa kluczowego var. Wraz z wejściem standardu ES6 wszystko wywróciło się do góry nogami....

Pojawiły się dwa nowe słowa kluczowe let i const.

Co, gdzie, jak, kiedy?

Różnice w użyciu możemy zauważyć w trzech obszarach:

Zasięg

Czyli gdzie w kodzie możemy użyć naszej zmiennej. W Javascripcie mamy trzy rodzaje zasięgu:

  • globalny
  • funkcyjny
  • blokowy

Globalny pozwala na odwołanie się do zmiennej z każdego miejsca w programie. Funkcyjny jak sama nazwa wskazuje dotyczy funkcji. Natomiast blokowy liczy się w zasięgu bloku kodu. Czym jest blok? Obszar kodu otoczony przez nawiasy wąsate, czyli np. wyrażenie if-else, funkcje, pętle itp. Jak to przekłada się na opisywane zagadnienie:

Var może mieć zasięg funkcyjny lub globalny. Zależy to od miejsca w jakim zmienna oznaczona tym słowem kluczowym zostanie zadeklarowana. Jeżeli zostanie umieszczona w funkcji to wtedy będziemy mieć do czynienia z zasięgiem funkcyjnym. Natomiast jeżeli zostanie utworzona poza funkcją, wtedy jej zasięg zmienia się na globalny.

// code function exampleFunction() { if(true) { var exampleVar = 2022; console.log("Console log inside block, variable value: ", exampleVar); // Console log inside block, variable value: 2022 } console.log("Console log outside block, variable value:", exampleVar); // Console log outside block, variable value: 2022 } exampleFunction(); // result Console log inside block, variable value: 2022 Console log outside block, variable value: 2022

Dla const i let mamy zasięg blokowy, czyli tak jak wyżej, zasięg działania dotyczy bloku w którym taka zmienna została utworzona:

  • const:
// code function exampleFunction() { if (true) { const exampleConst = 2022; console.log("Console log inside block, variable value: ", exampleConst); } console.log("Console log outside block, variable value:", exampleConst); } exampleFunction(); // result Console log inside block, variable value: 2022 Uncaught ReferenceError: exampleConst is not defined
  • let:
// code function exampleFunction() { if (true) { let exampleLet = 2022; console.log("Console log inside block, variable value: ", exampleLet); } console.log("Console log outside block, variable value:", exampleConst); } exampleFunction(); // result Console log inside block, variable value: 2022 Uncaught ReferenceError: exampleLet is not defined

Hoisting

Dla zmiennych oznaczonych var hoisting działa w standardowy sposób tzn. zmienne takiego typu są “windowane” w górę i dostęp do nich przed ich deklaracją w kodzie nie powoduje błędu. Inaczej jest w przypadku const i let. Niektóre źródła mylnie podają, że tak oznaczone zmienne nie ulegają hoistingowi. Nic bardziej mylnego. Hoisting również tam działa, ale mamy tam do czynienia z mechanizmem Temporal Dead Zone. W skrócie jest to obszar, pomiędzy wyniesioną w górę do początku najbliższego scope’a zmienną, a miejscem jej inicjalizacji. W tym obszarze zmienna jest niedostępna do momentu, aż do jego końca czyli gdy nastąpi przypisanie wartości.

// code console.log(exampleVar); var exampleVar = 5; // result undefined

W przypadku const i let odwołanie się do niezainicjalizowanej zmiennej spowoduje wystąpienie błędu ReferenceError: Cannot access 'exampleConst' before initialization. Możemy to przeanalizować sobie na fragmentach poniżej, gdzie umieściem zmienne w bloku if:

if (true) { console.log(exampleConst) const exampleConst = 5 } // ReferenceError: Cannot access 'exampleConst' before initialization
if (true) { console.log(exampleLet) const exampleLet = 5 } // ReferenceError: Cannot access 'exampleLet' before initialization

Zmiana wartości zmiennej

Kolejną różnicę zauważymy, gdy spróbujemy wykonać zmianę wartości. Taką możliwość daje nam var oraz let. Próba zmiany wartości zmiennej która została utworzona z użyciem const nie powiedzie się i spowoduje wyrzucenie błędu z wiadomością Uncaught TypeError: Assignment to constant variable. Chyba, że jest ona typem złożonym (obiekt, funkcja itp.) , wtedy dane w niej zawarte będą możliwe do zmiany. Dodatkowo w przypadku zmiennej typu const musimy pamiętać, by przypisać jej wartość przy tworzeniu. Inaczej dostaniemy błąd.

  • var
// code var exampleVar = 5; console.log(exampleVar); exampleVar = 10; console.log(exampleVar); // result 5 10
  • let
// code let exampleLet = 5; console.log(exampleLet); exampleLet = 10; console.log(exampleLet); // result 5 10
  • const
// code const exampleConst = 5; console.log(exampleConst); exampleConst = 10; console.log(exampleConst); // result 5 Uncaught TypeError: Assignment to constant variable.

Dodatkowo zmienna oznaczona jako var może być ponownie zadeklarowana tzn. możemy użyć tej samej nazwy do utworzenia nowej zmiennej zaraz obok poprzedniej. Najlepiej odda to poniższy snippet:

var testVar = 0; var testVar = 1;

Podobna sytuacja w przypadku const i let spowodowałaby wyrzucenie błędu SyntaxError: Identifier 'testVar' has already been declared.

Podsumowanie

Najlepszą praktyką jest ograniczanie zasięgu zmiennych oraz zapobieganie przypadkowej zmianie ich wartości. W miarę możliwości starajmy się używać const, gdzie tylko się da. Jeżeli sytuacja tego wymaga powinniśmy wspomagać się let. Natomiast jeżeli chodzi o var to jest to podejście, którego powinniśmy unikać.

Przydatne Linki: