Dziś będzie krótko i na temat! Od jakiegoś już czasu ES6 (nowa wersja języka JavaScript znana też jako ECMAScript 6/2015 - pod tym pojęciem kryją się też wszystkie późniejsze aktualizacje) jest przez nas szeroko używany na codzień. Przynajmniej mam taką nadzieję… Zanim jednak nastąpiła ta zmiana, podczas tworzenia funkcji stosowaliśmy często wzorzec jednego var (ang. single var pattern). Sam byłem osobą, która zalecała jego użycie!

Teraz, kiedy w najnowszej wersji JS mamy do dyspozycji nowe sposoby deklarowania zmiennych oraz inny ich zakres, można się zastanowić czy stosowanie tego wzorca wciąż ma sens… Ja przynajmniej miałem na początku chwilę zwątpienia z tym związaną. Postaram się więc dziś udzielić odpowiedzi na to pytanie!

Wzorzec jednego var (single var pattern)

Zanim przejdę do rozważań na temat sensowności dalszego używania wzorca jednej zmiennej myślę, że warto przypomnieć na czym on polegał. Dodać też należy dlaczego warto było go stosować.

Zakres zmiennych

Myślę, że najlepiej odpowiedział na te pytania Douglas Crockford, autor m.in. JSLint (najpopularniejszego swego czasu narzędzia do analizy kodu). Oto co napisał kiedyś na ten temat:

[…] because JavaScript does not have block scope, it is wiser to declare all of a function’s variables at the top of the function. It is recommended that a single var statement be used per function.

Czyli, w wolnym tłumaczeniu, w ES5 nie istniało pojęcie zakresu blokowego (ang. block scope). Jedynym zakresem dostępnym w języku JavaScript był zakres funkcji. Jeśli więc zadeklarowałeś coś, na przykład, w bloku wyrażenia if to było to dostępne również poza tym blokiem:

(function () {
  var a = 1;

  if (true) {
    var b = 2;
  }

  console.log('a = ' + a); // a = 1
  console.log('b = ' + b); // b = 2
}());

Zadeklarowanie więc zmiennej b na początku funkcji (a nie wewnątrz bloku, który i tak jej nie ogranicza) pozwalało na wyeliminowanie ewentualnych pomyłek.

Hoisting

Innym powodem dla którego wzorzec jednego var był zalecany jest hoisting. Powodował on, że wszystkie deklaracje zmiennych przenoszone były, podczas kompilacji, na początek funkcji. Niestety nie są one przenoszone razem z ich inicjalizacją, co może powodować kłopoty:

(function () {
  var a = 1;

  console.log('a = ' + a); // a = 1
  console.log('b = ' + b); // b = undefined

  if (true) {
    var b = 2;
  }
}());

Jak możesz zauważyć w linii numer pięć, zmienna b ma wartość undefined. Znaczy to, że istnieje już ona w tym momencie, nie jest jednak jeszcze zainicjowana wartością. Inicjalizacja następuje dopiero w linii numer osiem.

Oznacza to w zasadzie, że podczas kompilacji kod, który piszemy co nieco się zmieniał. Dla powyższego przykładu zmieniłby się on na mniej więcej taki:

(function () {
  var a = 1,
      b;

  console.log('a = ' + a); // a = 1
  console.log('b = ' + b); // b = undefined

  if (true) {
    b = 2;
  }
}());

Dlatego też, w przypadku ES5, lepiej było wyręczyć kompilator i samemu przenieść deklaracje. Dzięki temu zyskiwaliśmy pewność, że nasz kod jest zgodny z tym co później zostanie “wyplute” przez kompilator.

Czy w ES6 wzorzec jednego var ma wciąż sens?

No dobra… Ale teraz, w dobie ES6 mamy, poza var są jeszcze dwa inne sposoby na deklarację zmiennych.

Wyrażenia let oraz const pozwalają nam na deklarowanie zmiennych, która ograniczone są (za pomocą nawiasów klamrowych) do obejmującego je bloku kodu. Poza danym blokiem zmienne te są niewidoczne. Dla porządku spójrz zresztą na przykład:

(function () {
  if (true) {
    const a = 1;
    let b = 2;

    a = 2 // syntax error: 'a' is read-only
  }

  console.log(a); // reference error: can't find variable: 'a'
  console.log(b); // reference error: can't find variable: 'b'
}());

Jak widzisz, teraz jest to wszystko dużo bardziej przewidywalne… Co więc z zasadą mówiąc o deklarowaniu wszystkich zmiennych na początku funkcji? Moim zdaniem w tym momencie jest to już zupełnie nie potrzebne! Skoro zmienne deklarowane za pomocą let oraz const ograniczone są do bloku kodu, możemy i powinnyśmy z tego korzystać. Skoro w ich przypadku nie występują już opisane wyżej nieprzewidziane efekty uboczne, nie ma się przed czym bronić…

Podsumowanie

Pewnie dla wielu z Was to co dziś opisałem jest “oczywistą oczywistością”… Ja jednak na początku mojej przygody z ES6 musiałem się nad tym chwilę zastanowić. Stąd też pomysł na ten post - może ktoś ma podobny dylemat jak ja w tamtym czasie? Wydaje mi się, że niniejszym wpisem rozwiałem wszelkie wątpliwości…

P.S. To prawdopodobnie ostatni wpis w 2016 roku (być może jeszcze coś napiszę ale nie obiecuję)… Do przeczytania więc w styczniu 2017!