Menu
Русский

Синтаксис JavaScript и точки с запятой: правила ASI

Разбираем синтаксис JavaScript — инструкции, блоки, выражения — и честно говорим про точки с запятой: как работает ASI и где он реально подставляет подножку.

Код на JavaScript — это последовательность инструкций

Программа на JavaScript — это набор инструкций (statements), которые движок выполняет сверху вниз, одну за другой. Объявление переменной — инструкция. Вызов функции — инструкция. Блок if или for — тоже инструкция. Между собой инструкции разделяются точкой с запятой.

index.js
Output
Click Run to see the output here.

Три инструкции, три точки с запятой. Движок выполнит сначала первую, потом вторую, потом третью. Ничего сложного.

Пробелы и отступы для парсера вообще ничего не значат. Можно написать все три инструкции в одну строку, разделив точками с запятой, — результат будет тот же самый. Отступы мы делаем для людей, а не для движка.

Блоки: как группировать инструкции в JavaScript

Фигурные скобки { } образуют блок — группу инструкций, которую движок воспринимает как единое целое. Блоки встречаются на каждом шагу: тело функции, ветки if, тело цикла.

index.js
Output
Click Run to see the output here.

Два вызова console.log внутри фигурных скобок — это тело if. Обратите внимание: после закрывающей } точки с запятой нет, ведь блок — это не инструкция, которую нужно завершать. Инструкция if целиком заканчивается там, где заканчивается её блок.

На этом часто спотыкаются те, кто пришёл из языков, где после каждой } принято ставить ;. В javascript ни отдельный блок, ни блок управляющей конструкции точки с запятой не требуют.

Выражения и инструкции: в чём разница

С этим различием лучше разобраться сразу: выражения вычисляют значение, а инструкции выполняют действие.

  • 2 + 3 — это выражение. Его значение — 5.
  • let x = 2 + 3; — это инструкция. Она объявляет переменную и присваивает ей результат вычисления выражения.
  • console.log("hi") — это выражение (вызов функции возвращает undefined), но если написать его отдельной строкой с точкой с запятой, получится инструкция-выражение.
index.js
Output
Click Run to see the output here.

В большинстве случаев думать об этом не приходится. Но разница становится важной, когда речь заходит о стрелочных функциях, тернарных выражениях и о ситуациях, где JavaScript ждёт выражение, а вы подсунули ему инструкцию (или наоборот).

Нужны ли точки с запятой в JavaScript

Скажу как есть: в JavaScript работает механизм под названием Automatic Semicolon Insertion (ASI) — он сам расставляет недостающие точки с запятой на большинстве переносов строк. Поэтому вот такой код спокойно запустится:

index.js
Output
Click Run to see the output here.

Без точек с запятой — и никакой ошибки. ASI смотрит на каждый перенос строки и задаёт себе вопрос: «А может ли следующий токен быть продолжением текущей инструкции?» Если нет — подставляет точку с запятой.

Благодаря ASI в дикой природе уживаются два легитимных стиля:

  • Точки с запятой везде. Большинство кодовых баз, большинство туториалов, большинство style guide'ов.
  • Без точек с запятой. Встречается в современных проектах (Standard style, часть React-кодобаз). Держится на ASI плюс паре защитных приёмов.

Оба варианта рабочие. Ни один из них не «неправильный». А вот что реально плохо — это непоследовательность: смешивание стилей в рамках одного файла превращает отлов багов в квест.

Подводные камни ASI

В ~99% случаев ASI делает именно то, что нужно. Оставшийся 1% — это строки, которые начинаются с токена, способного продолжить предыдущую строку. Главные возмутители спокойствия: [, (, `, +, - и /.

Смотрите:

index.js
Output
Click Run to see the output here.

Казалось бы, x и y должны поменяться местами. Но ASI не вставляет точку с запятой перед [x, y], потому что конструкция 10[x, y] синтаксически валидна — это обращение по индексу к числу 10. Парсер склеивает вторую и третью строки в одно большое выражение, и в итоге вы ловите ошибку времени выполнения или мусор на выходе.

Лечится это либо точкой с запятой в конце второй строки:

index.js
Output
Click Run to see the output here.

Или же, если вы пишете в стиле без точек с запятой, ставьте ведущую точку с запятой в начале «опасной» строки:

index.js
Output
Click Run to see the output here.

Ведущая ; — это защитный приём, которым пользуются в кодовых базах без точек с запятой. Выглядит непривычно, пока не поймёшь, зачем она там.

Ловушка с return

Один случай с ASI стоит запомнить наизусть — он порождает тихие баги. Если поставить перенос строки сразу после return, ASI подставит туда точку с запятой, и функция вернёт undefined:

index.js
Output
Click Run to see the output here.

Автор хотел вернуть объект. Но ASI увидел return в конце строки и на месте же завершил инструкцию. Литерал объекта превратился в недостижимый код.

Решение — начинать возвращаемое значение на той же строке, что и return:

index.js
Output
Click Run to see the output here.

То же правило работает и для throw, break, continue, yield — не переносите строку между ключевым словом и его значением.

Простое правило, которое работает

Если вы только начинаете, самый надёжный путь такой:

  1. Ставьте точки с запятой явно в конце каждой инструкции.
  2. Не ставьте их после закрывающей } у if, for, while, объявлений функций и тел классов.
  3. Ставьте их после }, когда это объектный литерал или функциональное выражение, присвоенное переменной: const f = function() {};.
  4. Подключите форматтер (Prettier, ESLint со стилистическим правилом) и забудьте об этом вопросе. Форматтер сам приведёт код к тому стилю, о котором договорилась команда.
index.js
Output
Click Run to see the output here.

Это не какой-то незыблемый закон — просто соглашение, которого придерживается большинство JavaScript-кода, что вы встретите. Следуйте ему, пока у вас не появится веская причина поступить иначе.

Регистр символов и идентификаторы

Здесь же пара небольших правил синтаксиса JavaScript:

  • JavaScript чувствителен к регистру. userName, username и UserName — это три разных идентификатора.
  • Идентификаторы могут содержать буквы, цифры, _ и $, но не могут начинаться с цифры. И нельзя использовать зарезервированные слова вроде class, return или function.
index.js
Output
Click Run to see the output here.

Символ $ технически разрешён, но по традиции его оставляют под нужды библиотек (исторически его активно использовал jQuery, некоторые шаблонизаторы применяют до сих пор). Сами вы его себе в именах, скорее всего, выбирать не будете.

Дальше: строгий режим

Современный JavaScript негласно работает в более жёсткой версии языка — так называемом строгом режиме (strict mode), который превращает ряд вольностей в настоящие ошибки. ES-модули и тела классов по умолчанию уже строгие, но полезно разобраться, что именно при этом меняется — об этом следующая страница.

Часто задаваемые вопросы

Нужны ли вообще точки с запятой в JavaScript?

Формально — нет. В JavaScript есть механизм ASI (Automatic Semicolon Insertion), который сам расставляет их в большинстве переносов строк. Но на практике команды почти всегда пишут их явно: у ASI есть пара неприятных случаев — когда строка начинается с [, (, `, +, - или /, движок склеивает её с предыдущей и ломает логику. Самый разумный вариант — выбрать один стиль и доверить его Prettier.

Что такое автоматическая расстановка точек с запятой (ASI)?

ASI — это правило, по которому JavaScript сам дописывает пропущенную точку с запятой в конце строки, если следующий токен не может быть продолжением текущей инструкции. Именно поэтому let x = 1 без точки с запятой работает нормально. Проблемы начинаются, когда следующая строка может быть продолжением предыдущей — например, стартует с [ или ( — и интерпретатор молча склеивает их в одну инструкцию.

Как устроена инструкция в JavaScript?

Инструкция (statement) — это одно действие: объявление переменной, вызов функции, блок if, цикл. Инструкции разделяются точками с запятой (явными или подставленными через ASI). Группы инструкций объединяются в блоки с помощью { }. Пробелы и отступы движок игнорирует полностью — форматирование нужно людям, а не парсеру.

Учитесь программировать с Coddy

НАЧАТЬ