Jedną z najważniejszych innowacji dodanych w ECMAScript 6 jest wprowadzenie zmiennych o zakresie blokowym deklarowanych poprzez słowo kluczowe let. Do tej pory w JavaScript istniał tylko funkcyjny zakres zmiennych (deklarowanych przez dobrze wszystkim znane słówko var), co w pewnych przypadkach wymagało stosowania niezbyt lubianych przez programistów domknięć. Wraz z nadejściem ES6 ta niedogodność odchodzi w niepamięć. 

Czym się różnią blokowy i funkcyjny zakres zmiennych

Do tej pory w JavaScript, aby "zamknąć" zmienną w zakresie lokalnym musieliśmy użyć do tego funkcji (tzw. domknięcie) - np:

var a = 0; //zmienna globalna

(function () {
    var a = 5; //zmienna lokalna
})();

Blok kodu - taki, jak pętla, czy instrukcja warunkowa - nie są funkcjami, więc nie tworzą lokalnego zakresu dla zmiennych:

var i = 'X'; //zmienna globalna

for (var i = 0; i < 10; i++) { // nadpisanie tej samej zmiennej globalnej
    //jakieś działania
}
console.log(i); //10

Gdy chcemy wydzielić osobny zakres zmiennych dla pętli w ES5, musimy użyć domknięcia:

var i = 'X';

(function () {
    for (var i = 0; i < 10; i++) { // zmiennna lokalna
        //jakieś działania
    }
})();

console.log(i); //X

ECMAScript 6 oszczędza nam takiego kombinowania. Jedyne, co musimy zrobić, to zamienić słówko kluczowe var na let i o domknięciach możemy praktycznie zapomnieć:

let i = 'X'; //zmienna globalna

for (let i = 0; i < 10; i++) { // zmiennna lokalna w zakresie pętli
    //jakieś działania
}

console.log(i); //X

Tak samo zadziała to w przypadku każdego innego bloku kodu zamkniętego w nawiasach klamrowych {}.  Będzie tak więc zarówno dla instrukcji warunkowej:

var x = "globalna";
if (x == "globalna"){
    let x = "lokalna";
    console.log(x); //lokalna
}
console.log(x); //globalna

jak i nawet w takim przypadku:

var x = "globalna";
{
    let x = "lokalna";
    console.log(x); // lokalna
}
console.log(x);//globalna

Trzeba przy tym pamiętać, że to nie jest tak, że słowem kluczowym let nie możemy zadeklarować zmiennej globalnej. Wręcz przeciwnie - powinniśmy to robić. Jeśli deklaracja let = "wartosc"; zostanie umieszczona w zakresie globalnym, będzie ona dostępna z poziomu całego kodu - o ile tylko nie zostanie nadpisana kolejną deklaracją let = "wartosc2"; wewnątrz bloku kodu. Tak więc poprzedni przykład powinien zostać prawidłowo zapisany w taki sposób:

let x = "globalna";
{
    let x = "lokalna";
    console.log(x); // lokalna
}
console.log(x);//globalna

Stałe

ES6 daje nam także do dyspozycji słowo kluczowe const służące do deklaracji stałych. const od let różni się w dwóch miejscach:

  • Przypisanie wartości musi nastąpić w momencie deklaracji
  • Nie ma możliwości przypisania jakiejkolwiek nowej wartości do stałej zadeklarowanej przy użyciu const

Dobrym nawykiem jest używanie let tylko wtedy, gdy faktycznie przypisujemy do zmiennej nowe wartości w trakcie wykonywania programu (na przykład liczniki w pętlach). Do wszystkich innych przypadków powinniśmy stosować const.

Trzeba pamiętać, że choć nie możemy nadpisać stałej zadeklarowanej przy użyciu const (zostanie wyrzucony błąd):

const x = [1, 2, 3];
x = [3]; //błąd!

const y = {value: 42};
y = 5; //błąd!

to możemy zrobić coś takiego:

const x = [1, 2, 3];
x[0] = 3; //brak błędu

const y = {value: 42};
y.value = 0; //brak błędu
y.newValue = 11; //brak błędu

Dzieje się tak dlatego, że stałe nie przechowują obiektów ani tablic, a jedynie referencje do nich. I choć nie możemy przypisać stałym nowych referencji, to możemy zmienić same tablice czy obiekty bez wyrzucenia żadnego błędu. Dzieje się tak,  gdyż referencje, a więc to, co przechowuje stała, pozostają niezmienne.

Let i const zastępują var w 99% procentach przypadków

Oczywiście, jak to w internecie bywa, powyższa liczba jest z głębi otworu o czteroliterowej nazwie wyjęta i nie poparta żadnymi badaniami. Nie zmienia to jednak faktu, że gdy zaczniecie pisać kod w ES6, słów kluczowych let i const będziecie (a przynajmniej powinniście) używać ich dużo częściej niż poczciwego var. W standardzie ECMAScript 6 var staje się elementem języka do zadań specjalnych i jego użycie może być zasadne jest tylko w pewnych szczególnych przypadkach (choć prawdopodobnie nawet się na takie nie natkniecie).

Mam nadzieję, że widzicie zalety stosowania blokowego zakresu zmiennych nad funkcyjnym. Dla jasności są to przede wszystkim:

  • Mniejsza szansa na przypadkowe nadpisanie zmiennej globalnej
  • Możliwość wykorzystywania tej samej literki jako iteratora wielu pętli bez żadnych obaw
  • Mniejsze ryzyko wycieków pamięci (zmienne blokowe są usuwane z pamięci po wykorzystaniu)