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)