Dzisiaj na tapetę weźmiemy dość podstawową kwestię - postaram się przedstawić Ci jak wygląda zakres zmiennych JavaScript. Tym sposobem przechodzimy do kolejnego elementu wiedzy potrzebnej aby zdać egzamin MCSD 70-480 - na stronach Microsoftu jest on opisany następująco:

Establish the scope of objects and variables

This objective may include but is not limited to: define the lifetime of variables; keep objects out of the global namespace; use the “this” keyword to reference an object that fired an event; scope variables locally and globally

Dzisiejszy post powinien więc załatwić część “scope variables locally and globally”. Przejdźmy zatem do rzeczy!

Zmienne globalne i zmienne lokalne

W większości języków, takich jak na przykład C#, zakres zmiennych ogranicza się do bloku, w którym zmienna się znajduje (ograniczonym na przykład klamrami - takim blokiem może być więc metoda, klasa, czy kod wykonywany w pętli).

W JavaScript jest inaczej! Zmienna zadeklarowana wewnątrz bloku kodu, jest widoczna poza nim (jest jeden wyjątek ale o tym za chwilę) - na potwierdzenie taki oto przykład:

for (var i = 0; i < 10; i++) {
  // jakis kod
}

console.log(i); // 10

Mamy tutaj deklarację zmiennej ‘i’, która jest w pętli iterowana i na koniec uzyskuje wartość 10. Jednak w piątej linii mamy próbę odwołania się do tej zmiennej poza zakresem pętli - w JavaScript zostanie po prostu wyświetlona wartość 10, ponieważ zmienna ‘i’ wciąż istnieje mimo zakończenia pętli.

Takie zachowanie się zmiennych może powodować problemy, szczególnie dla osób przyzwyczajonych do języków, w których zakres dostępu do zmiennej definiują bloki kodu. Należy więc zapamiętać, że wszystkie zmienne deklarowane w języku JavaScript mają zakres globalny!!

Zakres funkcji

Jest jednak wyjątek od tej reguły - mowa o zakresie funkcji. Wszystkie zmienne zadeklarowane (przy użyciu słowa kluczowego ‘var’) wewnątrz funkcji widoczne są tylko w jej wnętrzu (od początku do końca). Niby wszystko jasne ale okazuje się, że nie do końca… Spójrzmy na taki kod:

var something = 0;

function doSomething() {
  something = 1;
  console.log(something);

  var something = 2; // deklaracja wewnątrz funkcji!!!
  console.log(something);
}

doSomething();
console.log(something);

Ten przykład pokazuje, że z tym deklarowaniem wewnątrz funkcji też należy uważać… W powyższym kodzie, najpierw mamy deklarację zmiennej ‘something’ poza funkcją (czyli jest to zmienna globalna). Później wewnątrz funkcji ustawiamy jej wartość na 1 i wyświetlamy. W linii siódmej z kolei przesłaniamy zmienna globalną i również ją wyświetlamy. Na koniec wywołujemy funkcję i znów wyświetlamy zmienna globalną.

Co w tym dziwnego? A no to co się stanie po wywołaniu takiego kodu. Najpierw wyświetli się alert z cyfrą “1” (nic niezwykłego), później cyfra “2” (też normalka). Na koniec jednak wyświetla się “0”! Okazuje się, że jeśli wewnątrz funkcji przesłonimy zmienna globalną (gdziekolwiek w funkcji, nawet na jej końcu), wszystkie operacje wewnątrz funkcji odnoszą się do tej przesłoniętej zmiennej a nie do zmiennej globalnej. Dla pewności możemy sprawdzić jeszcze taki kod:

var something = 0;

function doSomething() {
  something = 1;
}

doSomething();
console.log(something);

Tutaj, wewnątrz funkcji nie ma przesłonięcia zmiennej ‘something’, dlatego wszystko działa tak jak w “normalnym” języku - kod wyświetli cyfrę “1”.

Hoisting

Zakres zmiennych JavaScript, przedstawiony powyżej powiązany jest ściśle z czymś co nazywamy “hoisting”. Warto wiedzieć co to jest by uniknąć niespodziewanych błędów.

Generalnie hoisting to taki mechanizm w języku JavaScript, który polega na tym, że silnik JS wyszukuje wszystkie deklaracje zmiennych (i funkcji) w danej funkcji i przenosi je na początek. Jeśli to zbyt zagmatwane to może przykład będzie bardziej pomocny:

function hoisting() { console.log(x); // undefined
  if (true) {
    var x = 1;
  }

  console.log(x); // 1
}

Jak widzisz, w funkcji hoisting mamy blok if . Deklaruję w nim zmienną x i inicjuję wartością 1 . Najpierw spójrz na koniec - polecenie console.log wyświetli właśnie wartość 1. Tutaj myślę, że nie ma nic dla Ciebie niezwykłego.

Ciekawiej jest na początku. Tutaj console.log wyświetla wartość undefined co oznacza, że zmienna już na tym etapie istnieje! Czyli silnik JS przekształcił powyższy kod mniej więcej na taki jak poniżej:

function hoisting() {
  var x;

  console.log(x); // undefined

  if (true) {
    x = 1;
  }

  console.log(x); // 1
}

Co ważne i widoczne powyżej, hoisting powoduje przeniesienie tylko deklaracji. Przypisanie pozostaje w tym samym miejscu.

Z powodu występowania hoistingu dobrą praktyką jest deklarowanie wszystkich zmiennych na początku funkcji. Unikniesz w ten sposób niepotrzebnego zdziwienia, że coś nie działa tak jak oczekujemy.

Zakres zmiennych JavaScript - podsumowanie

Generalnie zakres zmiennych JavaScript, to temat z którym należy uważać jeśli jest się przyzwyczajonym do “normalnych” języków programowania. To wszystko na dziś, mam nadzieję, że przykłady są wyczerpujące i wszystko było zrozumiale wytłumaczone ;)

Zapraszam również na kolejne odcinki “kursu” do egzaminu 70-480.