JavaScript nabízí tři způsoby, jak deklarovat proměnné:
var
, let
a const
. Pro mnoho
programátorů není úplně jasné, kdy kterou z nich použít, většina
tutoriálů a linterů vás nutí používat je špatně. Pojďme si ukázat,
jak psát čistší a srozumitelnější kód bez zbytečných pravidel, která
nám ve skutečnosti nepomáhají.
Začněme tím nejnebezpečnějším
JavaScript má jednu zákeřnou vlastnost: pouhým opomenutím deklarace
proměnné můžete nevědomky používat globální proměnnou. Stačí
zapomenout na var
, let
nebo const
:
function calculatePrice(amount) {
price = amount * 100; // Opomenutí! Chybí 'let'
return price; // Používáme globální proměnnou 'price'
}
function processOrder() {
price = 0; // Používáme tu samou globální proměnnou!
// ... nějaký kód volající calculatePrice()
return price; // Vracíme úplně jinou hodnotu, než čekáme
}
Tohle je noční můra každého vývojáře – kód funguje zdánlivě správně, dokud nezačne někde jinde v aplikaci něco záhadně selhávat. Debugování takových chyb může zabrat hodiny, protože globální proměnná může být přepsána kdekoliv v aplikaci.
Proto je naprosto zásadní vždy deklarovat proměnné pomocí
let
nebo const
.
Zapomeňte na var
Klíčové slovo var
je v JavaScriptu od jeho počátku v roce
1995 a nese s sebou pár problematických vlastností, které byly v době
vzniku jazyka považovány za features, ale časem se ukázaly jako zdroj mnoha
chyb. Po dvaceti letech vývoje jazyka se autoři JavaScriptu rozhodli tyto
problémy řešit – ne opravou var
(kvůli zachování zpětné
kompatibility), ale představením nového klíčového slova let
v ES2015.
Na internetu najdete spoustu článků rozebírajících problémy
var
do nejmenších detailů. Ale víte co? Není potřeba se
v tom babrat. Berme var
prostě jako překonaný archaismus a
pojďme se soustředit na moderní JavaScript.
Kdy použít let
let
je moderní způsob deklarace proměnných
v JavaScriptu.
Příjemné je, že proměnná existuje vždy pouze uvnitř bloku kódu (tedy mezi složenými závorkami), kde byla definována. To dělá kód předvídatelnější a bezpečnější.
if (someCondition) {
let temp = calculateSomething();
// temp je dostupná jen zde
}
// temp už zde neexistuje
V případě cyklů je deklarace přísně vzato umístěna před složenými závorkami, ale nenechte si tím zmást, proměnná existuje jen v cyklu:
for (let counter = 0; counter < 10; counter++) {
// Proměnná counter existuje jen v cyklu
}
// counter už zde nejsou dostupné
Kdy použít const
const
slouží k deklarování konstant. Typicky jde
o důležité hodnoty na úrovni modulu nebo aplikace, které se nikdy
nemají měnit:
const PI = 3.14159;
const API_URL = 'https://api.example.com';
const MAX_RETRY_ATTEMPTS = 3;
Je ale důležité pochopit jeden klíčový detail: const pouze zabraňuje přiřazení nové hodnoty do proměnné – neřeší, co se děje s hodnotou samotnou. Tento rozdíl se projevuje zejména u objektů a polí (pole je ostatně také objekt) – const z nich nedělá immutable objekty, tj. nezabraňuje změnám uvnitř objektu:
const CONFIG = {
url: 'https://api.example.com',
timeout: 5000
};
CONFIG.url = 'https://api2.example.com'; // Toto funguje!
CONFIG = { url: 'https://api2.example.com' }; // Toto vyhodí TypeError!
Pokud potřebujete skutečně neměnný objekt, musíte jej nejprve zmrazit.
Dilema let
vs
const
Nyní se dostáváme k zajímavější otázce. Zatímco u var
vs let
je situace jasná, použití const
je
předmětem mnoha diskuzí v komunitě. Většina tutoriálů, style-guides a
linterů prosazuje pravidlo „používej const
všude, kde
můžeš“. Takže použití const
vídáme zcela běžně
v tělech funkcí nebo metod.
Pojďme si vysvětlit, proč je tato populární „best practice“ ve skutečnosti anti-pattern, který dělá kód méně čitelný a zbytečně svazující.
Přístup „pokud se proměnná v kódu nepřepisuje, měla by být
deklarována jako const
“ se na první pohled jeví logický.
Proč by jinak bůh stvořil const
? Čím víc „konstant“, tím
bezpečnější a předvídatelnější kód, že? A navíc rychlejší,
protože ho kompilátor může lépe optimalizovat.
Jenže celý tento přístup je ve skutečnosti nepochopení toho, k čemu konstanty slouží. Jde především o komunikaci záměru – opravdu chceme sdělit ostatním vývojářům, že do této proměnné se už nesmí nic přiřadit, nebo do ní jen náhodou v současné implementaci nic nepřiřazujeme?
// Skutečné konstanty - hodnoty, které jsou konstantní ze své podstaty
const PI = 3.14159;
const DAYS_IN_WEEK = 7;
const API_ENDPOINT = 'https://api.example.com';
// vs.
function processOrder(items) {
// Toto NEJSOU konstanty, jen náhodou je nepřepisujeme
const total = items.reduce((sum, item) => sum + item.price, 0);
const tax = total * 0.21;
const shipping = calculateShipping(total);
return { total, tax, shipping };
}
V prvním případě máme hodnoty, které jsou konstantami ze své
podstaty – vyjadřují neměnné vlastnosti našeho systému nebo důležitá
konfigurační data. Když někde v kódu vidíme PI
nebo
API_ENDPOINT
, okamžitě chápeme, proč jsou tyto hodnoty
konstanty.
V druhém případě používáme const
jen proto, že zrovna
teď náhodou hodnoty nepřepisujeme. Ale není to jejich podstatná
vlastnost – jsou to běžné proměnné, které bychom v příští
verzi funkce klidně mohli chtít změnit. A když to budeme chtít udělat,
const
nám v tom bude zbytečně bránit.
V dobách, kdy byl JavaScript jeden velký globální kód, mělo smysl
snažit se zabezpečit proměnné proti přepsání. Ale dnes píšeme kód
v modulech a třídách. Dnes je běžné a správné, že scope je malá
funkce a v jejím rámci vůbec nemá smysl rozdíl mezi let
a
const
řešit.
Protože to vytváří naprosto zbytečnou kognitivní zátěž:
- Programátor musí při psaní přemýšlet: „Budu tuhle hodnotu měnit? Ne? Tak musím dát const…“
- Čtenáře to ruší! Vidí v kódu
const
a ptá se: „Proč je tohle konstanta? Je to nějaká důležitá hodnota? Má to nějaký význam?“ - Za měsíc potřebujeme hodnotu změnit a musíme řešit: „Můžu změnit const na let? Nespoléhá na to někdo?“
Používejte jednoduše let
a tyto otázky nemusíte vůbec
neřešit.
Ještě horší je, když toto rozhodnutí dělá automaticky linter. Tedy
když linter „opraví“ proměnné na const, protože vidí jen jedno
přiřazení. Čtenář kódu pak zbytečně přemýšlí: „Proč tady musí
být tyto proměnné konstanty? Je to nějak důležité?“ A přitom to není
důležité – je to jen shoda okolností. Nepoužívejte v ESLint pravidlo
prefer-const
!
Mimochodem, argument o optimalizaci je mýtus. Moderní JavaScript engine
(jako V8) dokáže snadno detekovat, zda je proměnná přepisována nebo ne,
bez ohledu na to, jestli byla deklarována pomocí let
nebo
const
. Takže používání const
nepřináší
žádný výkonnostní benefit.
Implicitní konstanty
V JavaScriptu existuje několik konstrukcí, které implicitně vytvářejí
konstanty, aniž bychom museli použít klíčové slovo const
:
// importované moduly
import { React } from 'react';
React = something; // TypeError: Assignment to constant variable
// funkce
function add(a, b) { return a + b; }
add = something; // TypeError: Assignment to constant variable
// třídy
class User {}
User = something; // TypeError: Assignment to constant variable
Je to logické – tyto konstrukce definují základní stavební bloky
našeho kódu a jejich přepsání by mohlo způsobit chaos v aplikaci. Proto
je JavaScript automaticky chrání proti přepsání, stejně jako kdyby byly
deklarovány pomocí const
.
Konstanty ve třídách
Třídy byly do JavaScriptu přidány relativně nedávno (v ES2015) a
jejich funkcionalita teprve postupně dospívá. Například privátní členy
označené pomocí #
přišly až v roce 2022. Na podporu
konstant ve třídách JavaScript stále čeká. Prozatím můžete používat
static
, který ale není zdaleka to samé – označuje hodnotu
sdílenou mezi všemi instancemi třídy, nikoliv však neměnnou.
Závěr
var
nepoužívejte – je to přežitekconst
používejte pro skutečné konstanty na úrovni modulu- Ve funkcích a metodách používejte
let
– je to čitelnější a jasnější - Nenechte linter automaticky měnit
let
naconst
– není to o počtu přiřazení, ale o záměru
Komentáře
Ahoj #1
Zdravím.
Jsem Javascripťák/TS se znalostí Cčkových jazyků.
Souhlasím, že v moderním JS/TS nemá var moc místa.
Var/let/const je ale stejně věčný boj, který vzhledem k množství lidí, co proudí do IT a neznají důvody jejich zavedení, a kterým je to úplně u …, nemůžeme vyhrát.
Nejvíc mě dráždí, když někdo říká konstantám definovaným s const proměnná.
Co je ale důležité, tak použít const tam, kde se s hodnotou nebude hýbat a let tam, kde ano, používat Typescript a dodržovat typy. Rozeznat primitivní a složené typy.
Např. Const pro Promise, což je víceméně jednorázový identifikátor.
Nebo const pro objekty/pole, které se vytvoří jen jednou v životním cyklu daného scopu.
Nebo prostě kdekoliv, kde jde jen o přechodnou hodnotu, kterou použijeme a nezměníme.
Taco #2
Bod 2: ‚Čtenáře to ruší! Vidí v kódu const a ptá se: „Proč je tohle konstanta? Je to nějaká důležitá hodnota? Má to nějaký význam?“‘
Takto čtenář uvažovat nebude. Bude uvažovat: „ha, konstanta, nebude se mi to měnit, super“
Bod 3: ‚„Můžu změnit const na let? Nespoléhá na to někdo?“‘
Odpověď je jednoduchá, ano, vždycky můžeš změnit
const
nalet
, a nikdo na to nikdy nespoléhá.Já jsem všemi deseti pro to, aby člověk přemýšlel u pojmenovávání a voleb konstrukcí. Ale uvedená kritika používání
const
alet
mi přijde poněkud podezřelá.Napište komentář