Od kilku lat głośno było o wielkich zmianach w języku Javascript, które są wprowadzane w ramach standardu ES6. Jedną z najważniejszych i najpopularniejszych zmian to nowe słowa kluczowe pozwalające na deklarowanie zmiennych. Oprócz var
doszedł jeszcze const
i let
.
W projekcie nad którym mam okazję pracować stopniowo zaczęliśmy zastępować starego dobrego var
na nowe słowa kluczowe. Cały proces odbywa się w miarę naturalnie, i najczęściej po prostu zamiast var
stosowany jest let
, a w momencie gdy deklarujemy jakieś niezmieniające się wartości używamy const
. Jakie to proste, nic tylko kodzić :joy:
Ostatnio jednak zaczęliśmy się zastanawiać czy wszyscy członkowie zespołu mają podobne podejście do nowych słów kluczowych i czy nie warto ujednolicić tych zasad. No i jak się okazuje sprawa nie jest taka oczywista jak się to mogło wydawać :smiley:
Na początek jednak małe przypomnienie…
Czym różni się let i const od var
Jeśli temat już jest dla Ciebie znany to możesz skoczyć do kolejnego punktu :smiley:
Ogólnie rzecz biorąc tradycyjne deklarowanie zmiennych przy pomocy słowa kluczowego var
mogło powodować dziwne efekty uboczne w projektach realizowanych przez mniej doświadczonych programistów języka Javascript. Wiążą się z tym dwa aspekty, które różnią się od innych języków programowania: zakres zmiennych (scope) i hoisting.
Scope
Zmienna zadeklarowana wewnątrz funkcji jest widoczna tylko w jej obrębie.
function myFunction() { var a = 1; } myFunction(); console.log(a); // Uncaught ReferenceError: a is not defined
Ale… Jeśli nigdzie nie zadeklarujemy zmiennej i mimo wszystko przypiszemy do niej wartość wewnątrz funkcji, to staje się ona globalnie dostępna…
function myFunction() { a = 1; } myFunction(); console.log(a); // 1
Na szczęscie w tym wypadku rozwiązaniem jest użycie strict mode, którego polecam używać zawsze i wszędzie.
Zmienna zadeklarowana z użyciem var
wewnątrz pętli lub w warunku jest widoczna na zewnątrz tego bloku.
if (true) { var a = 2; } console.log(a); // 2
for (var a = 0; a < 5; a++) { // ... } console.log(a); // 5
I tutaj właśnie z pomocą przychodzą let
i const
, które zmieniają to działanie:
if (true) { const a = 2; } console.log(a); // Uncaught ReferenceError: a is not defined
for (let a = 0; a < 5; a++) { // ... } console.log(a); // Uncaught ReferenceError: a is not defined
Hoisting
W sumie nie wiem czy to bug czy feature, ale jest możliwość przypisania wartości do zmiennej zanim zostanie ona zadeklarowana ze słowem kluczowym var
. W moim odczuciu to strasznie głupie. Dlatego zawsze dbam aby wszystkie zmienne były deklarowane a początku bloku.
Dajmy przykład zachowania…
function myFunction() { a = 5; console.log(a); // 5 var a; } myFunction();
Możemy być pewni, że takie działannie przy użyciu słów kluczowych const
albo let
nie przejdzie.
function myFunction() { a = 5; console.log(a); // Uncaught ReferenceError: a is not defined let a; } myFunction();
Kiedy używać const, a kiedy let
Po krótkim przypomnieniu jakie są główne różnice między var
, a const
i let
warto przejść do najważniejszej częsci tego posta, a mianowicie zastanowienia się kiedy używać każdego z tych słów kluczowych.
Na początek przyjmijmy zasadę numer 1, jeśli już piszemy aplikację zgodnie z ES6: Rezygnujemy z używania var
. Od teraz możesz śmiało uznać var
jako DEPRECATED i pozbywać się go sukcesywnie ze swojego projektu :smiley:
Ustaliliśmy główną zasadę, var
to już historia.
const
Deklaracja zmiennej z użuciem const
musi mieć przypisaną wartość, bo inaczej zostanie rzucony wyjątek:
const a; // Uncaught SyntaxError: Missing initializer in const declaration
Zadeklarowanej zmiennej typu prymitywnego, nie pozwoli zmienić już tej wartości.
const a = 5; a = 7; // (Uncaught TypeError: Assignment to constant variable.)
Jednak w javascriptcie nic nie jest oczywiste :stuck_out_tongue:, tym razem też nie do końca const
oznacza to samo co w innych językach programowania. Używając const
nie deklarujemy stałej wartości, a stałą referencję. O ile deklarujemy zmienną z prymitywną wartością, np. typu Number
to już jej nie zmienimy bo zostanie rzucony wyjątek. Ale jeśli deklarowana zmienna przyjmuje obiekt albo tablicę to możemy spokojnie zmieniać ich zawartość.
const b = {}; const c = []; b.p = 3; c.push('a'); console.log(b); // Object {p: 3} console.log(c); // ["a"]
Tak naprawdę to wnosi ciekawe możliwości. Możemy dowolnie manipulować zawartością obiektu lub tablicy i mamy pewność, że ich referencja się nie zmieni. To znaczy, że możemy mieć pewność, że w żadnym miejscu tablica nagle nie zmieni się w null
, Number
lub cokolwiek innego. Dlatego do deklarowania obiektów i tablic powinniśmy używać słowa kluczowego const
.
Trzeba odejść od myślenia, że const
to jakaś zmienna konfiguracyjna lub coś w tym stylu co najczęściej deklarujesz DUŻYMI_LITERAMI na początku pliku. Dzięki temu, że const
to zwykła zmienna posiadająca stałą referencję, stanowi dodatkowe zabezpieczenie przed efektami ubocznymi, które mogą nie zostać zauważone od razu. Staraj się stosować const
jak najczęściej.
Jak stworzyć niezmienny obiekt?!
Są sytuacje, że chcemy aby obiekt lub tablica były całkowicie niemodyfikowalne, chociażby wtedy gdy chcemy je zastosować jako wspomniana wyżej „konfiguracja”. Możemy to załatwić funkcją Object.freeze
, która nie pozwoli na żadną modyfikację.
const CONFIG = Object.freeze({ URL: 'https://devenv.pl', NAME: 'DevEnv' }); CONFIG.NAME = 'replaces'; console.log(CONFIG); // Object {URL: "https://devenv.pl", NAME: "DevEnv"}
let
Tak naprawdę temat został wyczerpany podczas opisywania słowa kluczowego const
. Co tu więcej mówić… Używaj let
wszędzie tam, gdzie zmienna musi być zmieniana (WAT? :stuck_out_tongue: W sensie mutable variable), czyli np. w pętlach.
Trzy podstawowe zasady
- Zapomnij o
var
. - Tam gdzie to możliwe stosuj
const
. - Jeśli już musisz to zadeklaruj zmienną z użyciem
let
.