Bloki i zakres widoczności zmiennych

Blokiem kodu nazywamy ciąg instrukcji zawartych w nawiasach klamrowych {}. Może to być ciało funkcji albo blok wykonywany w instrukcjach złożonych. Jedną z najważniejszych zasad funkcjonowania bloków, jest widoczność zadeklarowanych w nich zmiennych.

Przykład:

> if (true) {
   var v=1;
   const c=1;
   let l=1;
  }
> v
1
> l
ReferenceError: l is not defined
> c
ReferenceError: c is not defined
>

Widzimy, że zmienna zadeklarowana słowem var jest widoczna poza blokiem w którym to wykonaliśmy (caiło instrukcji if), gdy tymczasem w przypadku let i const - mamy widoczność jedynie w obrębie ciała instrukcji/funkcji. W Javascript operuje się często pojęciem domknięcia (clousre) - które określa w jakim bloku jest zamknięta nasza zmienna. W przypadku zmiennych var domknięcie tworzą jedynie funkcje. Warto przy tym zwrócić uwagę na niebezpieczeństwo związane z zagnieżdżaniem bloków. Przeanalizujmy poniższy kod:

> var kolor1='red';
> var kolor2='blue';
> function komunikat(msg) {
   var kolor1='yellow';
   kolor2='black';
   console.log(msg,kolor1,kolor2);
 }
> console.log(kolor1,kolor2);
red blue
> komunikat('OK');
yellow black
> console.log(kolor1,kolor2);
red black

Wykonanie funkcji komunikatspowodowało zmianę wcześniej zadeklarowanej zmiennej kolor2, gdy tymczasem kolor1 zmienił się jedynie w ciele funkcji. Różnica bierze się z tego, że w ciele funkcji zadeklarowano zmienną lokalną kolor1 (użycie słowa kluczowego var). Tymczasem zmienna kolor2 jest zadeklarowana tylko raz i ona jest uzywana zarówno w ciele funkcji jak i na zewnątrz (jest to ta sama zmienna).

Problem w tym, że deklaracja zmiennej w Javascript następuje przy pierwszym jej użyciu i brak var w przypadku kolor2='black'; mógł wynikać z omyłkowego ominięcia var!

Tego problemu nie ma w trybie ścisłym ( "strict") skryptów. Wymagana jest wówczas pełna deklaracja zmiennych.

Poniższy skrypt zakończy się błędem - gdyż w pierwszej linii włączamy tryb ścisły:

"use strict";
a=1; // brakuje słowa var let lub const

Hoisting

Przyjrzyjmy się przykładowi:

function komunikat(msg) {
  if (kolor===undefined) {
     var kolor='black';
  }
  console.log(msg,kolor);
}
komunikat('OK');

W warunku instrukcji if sprawdzamy, czy zmienna kolor ma wartość niezdefiniowną. Jednak ta zmienna nie została jeszcze w ogóle zadeklarowana - dzieje się to w wierszu następnym. Czyli mamy do czynienia z błędem? Nie - gdyż zmienne zadeklarowane z użyciem słowa kluczowego var są widoczne w obrębie całej funkcji, w której znajduje się deklaracja (jeśli poza funkcją - deklarujemy tak zmienne globalne). Nazywa się to hoistingiem (http://poradnik.drogimex.pl/2017/05/20/hoisting-zmiennych-i-funkcji-w-javascript/, https://www.nafrontendzie.pl/zakres-zmiennych-javascript).

Nie dotyczy to zmiennych deklarowanych przy użyciu let i const. One są widoczne dopiero po zadeklarowaniu. Zmiana var w powyższym przykładzie na let/const powoduje błąd.