Код на JavaScript — это последовательность инструкций
Программа на JavaScript — это набор инструкций (statements), которые движок выполняет сверху вниз, одну за другой. Объявление переменной — инструкция. Вызов функции — инструкция. Блок if или for — тоже инструкция. Между собой инструкции разделяются точкой с запятой.
Три инструкции, три точки с запятой. Движок выполнит сначала первую, потом вторую, потом третью. Ничего сложного.
Пробелы и отступы для парсера вообще ничего не значат. Можно написать все три инструкции в одну строку, разделив точками с запятой, — результат будет тот же самый. Отступы мы делаем для людей, а не для движка.
Блоки: как группировать инструкции в JavaScript
Фигурные скобки { } образуют блок — группу инструкций, которую движок воспринимает как единое целое. Блоки встречаются на каждом шагу: тело функции, ветки if, тело цикла.
Два вызова console.log внутри фигурных скобок — это тело if. Обратите внимание: после закрывающей } точки с запятой нет, ведь блок — это не инструкция, которую нужно завершать. Инструкция if целиком заканчивается там, где заканчивается её блок.
На этом часто спотыкаются те, кто пришёл из языков, где после каждой } принято ставить ;. В javascript ни отдельный блок, ни блок управляющей конструкции точки с запятой не требуют.
Выражения и инструкции: в чём разница
С этим различием лучше разобраться сразу: выражения вычисляют значение, а инструкции выполняют действие.
2 + 3— это выражение. Его значение —5.let x = 2 + 3;— это инструкция. Она объявляет переменную и присваивает ей результат вычисления выражения.console.log("hi")— это выражение (вызов функции возвращаетundefined), но если написать его отдельной строкой с точкой с запятой, получится инструкция-выражение.
В большинстве случаев думать об этом не приходится. Но разница становится важной, когда речь заходит о стрелочных функциях, тернарных выражениях и о ситуациях, где JavaScript ждёт выражение, а вы подсунули ему инструкцию (или наоборот).
Нужны ли точки с запятой в JavaScript
Скажу как есть: в JavaScript работает механизм под названием Automatic Semicolon Insertion (ASI) — он сам расставляет недостающие точки с запятой на большинстве переносов строк. Поэтому вот такой код спокойно запустится:
Без точек с запятой — и никакой ошибки. ASI смотрит на каждый перенос строки и задаёт себе вопрос: «А может ли следующий токен быть продолжением текущей инструкции?» Если нет — подставляет точку с запятой.
Благодаря ASI в дикой природе уживаются два легитимных стиля:
- Точки с запятой везде. Большинство кодовых баз, большинство туториалов, большинство style guide'ов.
- Без точек с запятой. Встречается в современных проектах (Standard style, часть React-кодобаз). Держится на ASI плюс паре защитных приёмов.
Оба варианта рабочие. Ни один из них не «неправильный». А вот что реально плохо — это непоследовательность: смешивание стилей в рамках одного файла превращает отлов багов в квест.
Подводные камни ASI
В ~99% случаев ASI делает именно то, что нужно. Оставшийся 1% — это строки, которые начинаются с токена, способного продолжить предыдущую строку. Главные возмутители спокойствия: [, (, `, +, - и /.
Смотрите:
Казалось бы, x и y должны поменяться местами. Но ASI не вставляет точку с запятой перед [x, y], потому что конструкция 10[x, y] синтаксически валидна — это обращение по индексу к числу 10. Парсер склеивает вторую и третью строки в одно большое выражение, и в итоге вы ловите ошибку времени выполнения или мусор на выходе.
Лечится это либо точкой с запятой в конце второй строки:
Или же, если вы пишете в стиле без точек с запятой, ставьте ведущую точку с запятой в начале «опасной» строки:
Ведущая ; — это защитный приём, которым пользуются в кодовых базах без точек с запятой. Выглядит непривычно, пока не поймёшь, зачем она там.
Ловушка с return
Один случай с ASI стоит запомнить наизусть — он порождает тихие баги. Если поставить перенос строки сразу после return, ASI подставит туда точку с запятой, и функция вернёт undefined:
Автор хотел вернуть объект. Но ASI увидел return в конце строки и на месте же завершил инструкцию. Литерал объекта превратился в недостижимый код.
Решение — начинать возвращаемое значение на той же строке, что и return:
То же правило работает и для throw, break, continue, yield — не переносите строку между ключевым словом и его значением.
Простое правило, которое работает
Если вы только начинаете, самый надёжный путь такой:
- Ставьте точки с запятой явно в конце каждой инструкции.
- Не ставьте их после закрывающей
}уif,for,while, объявлений функций и тел классов. - Ставьте их после
}, когда это объектный литерал или функциональное выражение, присвоенное переменной:const f = function() {};. - Подключите форматтер (Prettier, ESLint со стилистическим правилом) и забудьте об этом вопросе. Форматтер сам приведёт код к тому стилю, о котором договорилась команда.
Это не какой-то незыблемый закон — просто соглашение, которого придерживается большинство JavaScript-кода, что вы встретите. Следуйте ему, пока у вас не появится веская причина поступить иначе.
Регистр символов и идентификаторы
Здесь же пара небольших правил синтаксиса JavaScript:
- JavaScript чувствителен к регистру.
userName,usernameиUserName— это три разных идентификатора. - Идентификаторы могут содержать буквы, цифры,
_и$, но не могут начинаться с цифры. И нельзя использовать зарезервированные слова вродеclass,returnилиfunction.
Символ $ технически разрешён, но по традиции его оставляют под нужды библиотек (исторически его активно использовал jQuery, некоторые шаблонизаторы применяют до сих пор). Сами вы его себе в именах, скорее всего, выбирать не будете.
Дальше: строгий режим
Современный JavaScript негласно работает в более жёсткой версии языка — так называемом строгом режиме (strict mode), который превращает ряд вольностей в настоящие ошибки. ES-модули и тела классов по умолчанию уже строгие, но полезно разобраться, что именно при этом меняется — об этом следующая страница.
Часто задаваемые вопросы
Нужны ли вообще точки с запятой в JavaScript?
Формально — нет. В JavaScript есть механизм ASI (Automatic Semicolon Insertion), который сам расставляет их в большинстве переносов строк. Но на практике команды почти всегда пишут их явно: у ASI есть пара неприятных случаев — когда строка начинается с [, (, `, +, - или /, движок склеивает её с предыдущей и ломает логику. Самый разумный вариант — выбрать один стиль и доверить его Prettier.
Что такое автоматическая расстановка точек с запятой (ASI)?
ASI — это правило, по которому JavaScript сам дописывает пропущенную точку с запятой в конце строки, если следующий токен не может быть продолжением текущей инструкции. Именно поэтому let x = 1 без точки с запятой работает нормально. Проблемы начинаются, когда следующая строка может быть продолжением предыдущей — например, стартует с [ или ( — и интерпретатор молча склеивает их в одну инструкцию.
Как устроена инструкция в JavaScript?
Инструкция (statement) — это одно действие: объявление переменной, вызов функции, блок if, цикл. Инструкции разделяются точками с запятой (явными или подставленными через ASI). Группы инструкций объединяются в блоки с помощью { }. Пробелы и отступы движок игнорирует полностью — форматирование нужно людям, а не парсеру.