Menu

Składnia JavaScript i średniki: zasady ASI

Jak działa składnia JavaScript: instrukcje, bloki, wyrażenia, a do tego cała prawda o średnikach, automatycznym wstawianiu średników i sytuacjach, w których to zawodzi.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Kod JavaScript to ciąg instrukcji

Program w JavaScript to lista instrukcji (statements), czyli poleceń, które silnik wykonuje od góry do dołu. Deklaracja zmiennej to instrukcja. Wywołanie funkcji to instrukcja. Blok if albo for to instrukcja. Instrukcje oddziela się od siebie średnikami.

Trzy instrukcje, trzy średniki. Silnik wykonuje pierwszą, potem drugą, potem trzecią. Nic trudnego.

Białe znaki i wcięcia nic nie znaczą dla parsera. Można by zapisać wszystkie trzy instrukcje w jednej linii, oddzielone średnikami, i działałyby identycznie. Wcięcia robimy dla ludzi, nie dla silnika.

Bloki grupują instrukcje

Nawiasy klamrowe { } tworzą blok, czyli grupę instrukcji, którą silnik traktuje jako całość. Bloki są wszędzie: w ciałach funkcji, gałęziach if, ciałach pętli.

Dwa wywołania console.log w nawiasach klamrowych to ciało instrukcji if. Zauważ, że po zamykającym } nie ma średnika: bloki nie są instrukcjami, które trzeba kończyć. Cała instrukcja if kończy się razem z blokiem.

To zaskakuje osoby przychodzące z języków, w których po każdym } ma stać ;. W JavaScript samodzielny blok ani blok sterujący go nie potrzebuje.

Wyrażenia a instrukcje

Jedno rozróżnienie warto zrozumieć wcześnie: wyrażenia (expressions) dają wartość, instrukcje coś robią.

  • 2 + 3 to wyrażenie. Jego wartość to 5.
  • let x = 2 + 3; to instrukcja. Deklaruje zmienną i przypisuje jej wynik wyrażenia.
  • console.log("hi") to wyrażenie (wywołanie funkcji zwraca undefined), ale zapisane w osobnej linii ze średnikiem staje się instrukcją wyrażeniową (expression statement).

Przez większość czasu nie musisz o tym myśleć. Ma to znaczenie, gdy spotkasz funkcje strzałkowe, operator trójargumentowy i miejsca, w których JavaScript oczekuje wyrażenia, a ty dajesz mu instrukcję (albo odwrotnie).

Kwestia średników

Szczera odpowiedź: JavaScript ma mechanizm automatycznego wstawiania średników (Automatic Semicolon Insertion, ASI), który wstawia brakujące średniki przy większości końców linii. Dlatego taki kod działa bez problemu:

Bez średników, bez błędu. ASI patrzy na każdy koniec linii i pyta: „czy następny token mógłby kontynuować bieżącą instrukcję?”. Jeśli nie, wstawia średnik.

Z powodu ASI w praktyce istnieją dwa uprawnione style:

  • Zawsze średniki. Większość projektów, większość tutoriali, większość przewodników stylu.
  • Bez średników. Używany w niektórych nowoczesnych projektach (styl Standard, część projektów w React). Opiera się na ASI i kilku sztuczkach obronnych.

Oba działają. Żaden nie jest „zły”. Zła jest za to niespójność: mieszanie stylów w jednym pliku utrudnia wyłapywanie błędów.

Kiedy ASI robi ci psikusa

ASI działa poprawnie w około 99% przypadków. Pozostały 1% to linie, które zaczynają się od tokenu mogącego kontynuować poprzednią linię. Problematyczne są [, (, `, +, - i /.

Spójrz na to:

Można by się spodziewać, że x i y zamienią się wartościami. Tymczasem ASI nie wstawia średnika przed [x, y], bo 10[x, y] jest składniowo poprawne (to indeksowanie liczby 10). Parser czyta linię 2 i 3 jako jedno duże wyrażenie, a ty dostajesz błąd w czasie działania albo bezsensowny wynik.

Poprawka to albo średnik na końcu linii 2:

Albo, jeśli piszesz w stylu bez średników, średnik na początku ryzykownej linii:

Ten początkowy ; to właśnie sztuczka obronna projektów bez średników. Wygląda dziwnie, dopóki nie wiesz, po co tam jest.

Pułapka z return

Jeden przypadek ASI warto zapamiętać, bo prowadzi do cichych błędów. Jeśli zaraz po return złamiesz linię, ASI wstawi tam średnik i funkcja zwróci undefined:

Autor chciał zwrócić obiekt. ASI zobaczyło return na końcu linii i od razu zakończyło instrukcję. Literał obiektu stał się nieosiągalnym kodem.

Poprawka polega na rozpoczęciu wartości w tej samej linii co return:

Ta sama zasada dotyczy throw, break, continue i yield: nie łam linii między słowem kluczowym a jego wartością.

Prosta zasada, która działa

Na początek najprościej jest tak:

  1. Pisz średniki jawnie na końcu każdej instrukcji.
  2. Nie stawiaj ich po zamykającym } bloków if, for, while, deklaracji funkcji ani ciał klas.
  3. Stawiaj je po } literału obiektu albo wyrażenia funkcyjnego przypisanego do zmiennej: const f = function() {};.
  4. Używaj formattera (Prettier, ESLint z regułą stylu) i przestań o tym myśleć. Formatter pilnuje tego, na co umówił się twój zespół.

Ta zasada nie jest prawem uniwersalnym, tylko konwencją, którą zobaczysz w większości czytanego kodu JavaScript. Trzymaj się jej, dopóki nie masz powodu, żeby tego nie robić.

Wielkość liter i identyfikatory

Jeszcze dwie drobne zasady, które pasują tutaj:

  • JavaScript rozróżnia wielkość liter. userName, username i UserName to trzy różne identyfikatory.
  • Identyfikatory mogą zawierać litery, cyfry, _ i $, ale nie mogą zaczynać się od cyfry. Nie mogą też być słowami zastrzeżonymi, takimi jak class, return czy function.

$ jest dozwolony, ale zwyczajowo zostawia się go na rzeczy specyficzne dla bibliotek (dawniej używało go jQuery, niektóre systemy szablonów nadal to robią). Rzadko wybierasz go sam.

Dalej: tryb ścisły

Nowoczesny JavaScript po cichu działa w bardziej rygorystycznym wariancie języka, zwanym trybem ścisłym (strict mode), który zamienia kilka niedbałych zachowań w prawdziwe błędy. Moduły ES i ciała klas są domyślnie w trybie ścisłym, ale warto wiedzieć, co to właściwie zmienia. O tym jest następna strona.

Najczęściej zadawane pytania

Czy w JavaScript trzeba pisać średniki?

Technicznie nie: JavaScript ma automatyczne wstawianie średników (ASI), które dodaje je za ciebie przy większości końców linii. W praktyce większość zespołów i tak pisze je jawnie, bo ASI ma kilka przypadków brzegowych (linie zaczynające się od [, (, `, +, -, /), w których działa źle. Wybierz styl i pozwól, żeby pilnował go formatter, na przykład Prettier.

Czym jest automatyczne wstawianie średników w JavaScript?

ASI to reguła, dzięki której JavaScript wstawia brakujący średnik na końcu linii, gdy następny token nie mógłby kontynuować bieżącej instrukcji. Dlatego let x = 1 działa bez średnika. Zawodzi, gdy linia zaczyna się od czegoś, co może kontynuować poprzednią linię, na przykład [ albo (, a JavaScript po cichu skleja obie linie w jedną.

Jak wygląda podstawowa składnia instrukcji w JavaScript?

Instrukcja to polecenie: deklaracja zmiennej, wywołanie funkcji, blok if, pętla. Instrukcje oddziela się średnikami (jawnymi albo wstawionymi). Bloki instrukcji umieszcza się w { }. Parser ignoruje białe znaki i wcięcia, więc formatowanie jest dla ludzi, nie dla silnika.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ