Spec-Zone.ru › JavaScript

Лексическая грамматика

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

Символы управления форматированием

Символы управления форматированием не имеют визуального представления, но используются для управления интерпретацией текста.

Кодовая точка Название Сокращение Описание
U+200C Нулевая ширина неразделителя <ZWNJ> Размещается между символами, чтобы предотвратить их соединение в лигатуры в некоторых языках (Wikipedia).
U+200D Нулевая ширина соединителя <ZWJ> Размещается между символами, которые обычно не соединяются, чтобы символы отображались в своем соединенном виде в некоторых языках (Wikipedia).
U+FEFF Маркер порядка байтов <BOM> Используется в начале скрипта, чтобы пометить его как Unicode и определить кодировку и порядок байтов текста (Wikipedia).

В исходном тексте JavaScript <ZWNJ> и <ZWJ> обрабатываются как части идентификатора, тогда как <BOM> (также называемый нулевой шириной неразрывного пробела <ZWNBSP>, когда он не находится в начале текста) обрабатывается как пробельный символ.

Пробельные символы

Пробельные символы улучшают читаемость исходного текста и разделяют токены друг от друга. Эти символы обычно не являются необходимыми для функциональности кода. Инструменты минификации часто используются для удаления пробелов, чтобы уменьшить объем передаваемых данных.

Кодовая точка Название Сокращение Описание Экранирующая последовательность
U+0009 Символ табуляции <TAB> Горизонтальная табуляция \t
U+000B Вертикальная табуляция <VT> Вертикальная табуляция \v
U+000C Перевод страницы <FF> Символ управления разрывом страницы (Wikipedia). \f
U+0020 Пробел <SP> Обычный пробел
U+00A0 Неразрывный пробел <NBSP> Обычный пробел, но без точки, где может произойти разрыв строки
U+FEFF Пробел нулевой ширины без разрыва <ZWNBSP> Когда маркер BOM не находится в начале скрипта, он является обычным пробельным символом.
Другие Другие пробельные символы Unicode <USP> Символы в общей категории "Space_Separator"

Примечание: Из этих символов со свойством "White_Space", но не входящих в общую категорию "Space_Separator", U+0009, U+000B и U+000C по-прежнему обрабатываются как пробелы в JavaScript; U+0085 NEXT LINE не имеет специальной роли; остальные становятся набором терминаторов строк.

Примечание: Изменения в стандарте Unicode, используемом движком JavaScript, могут повлиять на поведение программ. Например, ES2016 обновил эталонный стандарт Unicode с 5.1 до 8.0.0, что привело к перемещению U+180E MONGOLIAN VOWEL SEPARATOR из категории "Space_Separator" в категорию "Format (Cf)" и сделало его не пробельным. Впоследствии результат "\u180E".trim().length изменился с 0 на 1.

Терминаторы строк

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

Вне контекста лексической грамматики пробелы и терминаторы строк часто путают. Например, String.prototype.trim() удаляет все пробелы и терминаторы строк из начала и конца строки. Экранирование символьного класса \s в регулярных выражениях соответствует всем пробельным символам и терминаторам строк.

Только следующие кодовые точки Unicode обрабатываются как терминаторы строк в ECMAScript; другие символы разрыва строки обрабатываются как пробелы (например, Next Line, NEL, U+0085 считается пробелом).

Кодовая точка Название Сокращение Описание Экранирующая последовательность
U+000A Перевод строки <LF> Символ новой строки в системах UNIX. \n
U+000D Возврат каретки <CR> Символ новой строки в системах Commodore и ранних Mac. \r
U+2028 Разделитель строк <LS> Wikipedia
U+2029 Разделитель абзацев <PS> Wikipedia

Комментарии

Комментарии используются для добавления подсказок, заметок, предложений или предупреждений к коду JavaScript. Это может сделать его легче читать и понимать. Их также можно использовать для отключения кода, чтобы предотвратить его выполнение; это может быть ценным инструментом отладки.

JavaScript имеет два давно существующих способа добавления комментариев в код: однострочные и блочные комментарии. Кроме того, существует специальный синтаксис хешбенг-комментария.

Однострочные комментарии

Первый способ — это комментарий //; он делает весь текст, следующий за ним в той же строке, комментарием. Например:

function comment() {
  // This is a one line JavaScript comment
  console.log("Hello world!");
}
comment();

Блочные комментарии

Второй способ — это стиль /* */, который намного гибче.

Например, вы можете использовать его в одной строке:

function comment() {
  /* This is a one line JavaScript comment */
  console.log("Hello world!");
}
comment();

Вы также можете создавать многострочные комментарии, вот так:

function comment() {
  /* This comment spans multiple lines. Notice
     that we don't need to end the comment until we're done. */
  console.log("Hello world!");
}
comment();

Вы также можете использовать его в середине строки, если хотите, хотя это может сделать ваш код труднее читаемым, поэтому его следует использовать с осторожностью:

function comment(x) {
  console.log("Hello " + x /* insert the value of x */ + " !");
}
comment("world");

Кроме того, вы можете использовать его для отключения кода, чтобы предотвратить его выполнение, обернув код в комментарий, вот так:

function comment() {
  /* console.log("Hello world!"); */
}
comment();

В этом случае вызов console.log() никогда не произойдет, так как он находится внутри комментария. Таким образом можно отключить любое количество строк кода.

Блочные комментарии, содержащие хотя бы один терминатор строки, ведут себя как терминаторы строк при автоматической вставке точки с запятой.

Хешбенг-комментарии

Существует специальный третий синтаксис комментария — хешбенг-комментарий. Хешбенг-комментарий ведет себя точно так же, как однострочный (//) комментарий, за исключением того, что он начинается с #! и действителен только в самом начале скрипта или модуля. Обратите внимание, что перед #! не допускается пробел любого вида. Комментарий состоит из всех символов после #! до конца первой строки; разрешен только один такой комментарий.

Хешбенг-комментарии в JavaScript напоминают хешбенги в Unix, которые указывают путь к конкретному интерпретатору JavaScript, который вы хотите использовать для выполнения скрипта. До того, как хешбенг-комментарий был стандартизирован, он уже был де-факто реализован в хостах, не являющихся браузерами, таких как Node.js, где он удалялся из исходного текста перед передачей в движок. Пример:

#!/usr/bin/env node

console.log("Hello world");

Интерпретатор JavaScript будет рассматривать его как обычный комментарий — он имеет смысловое значение для оболочки только в том случае, если скрипт выполняется напрямую в оболочке.

Предупреждение: Если вы хотите, чтобы скрипты были запускаемыми напрямую в среде оболочки, закодируйте их в UTF-8 без BOM. Хотя BOM не вызовет проблем для кода, выполняемого в браузере — поскольку он удаляется при декодировании UTF-8 перед анализом исходного текста — оболочка Unix/Linux не распознает хешбенг, если перед ним стоит символ BOM.

Вы должны использовать только стиль комментария #! для указания интерпретатора JavaScript. Во всех остальных случаях используйте однострочный комментарий // (или многострочный комментарий).

Идентификаторы

Идентификатор используется для привязки значения к имени. Идентификаторы могут использоваться в различных местах:

const decl = 1; // Variable declaration (may also be `let` or `var`)
function fn() {} // Function declaration
const obj = { key: "value" }; // Object keys
// Class declaration
class C {
  #priv = "value"; // Private field
}
lbl: console.log(1); // Label

В JavaScript идентификаторы обычно состоят из буквенно-цифровых символов, подчеркиваний (_) и знаков доллара ($). Идентификаторы не могут начинаться с цифр. Однако идентификаторы JavaScript не ограничиваются только ASCII — допускаются и многие кодовые точки Unicode. А именно:

  • Символы начала могут быть любыми символами из категории ID_Start плюс _ и $.
  • После первого символа можно использовать любые символы из категории ID_Continue плюс U+200C (ZWNJ) и U+200D (ZWJ).

Примечание: Если по какой-то причине вам нужно самостоятельно разобрать исходный код JavaScript, не предполагайте, что все идентификаторы следуют шаблону /[A-Za-z_$][\w$]*/ (т.е. только ASCII)! Диапазон идентификаторов можно описать регулярным выражением /[$_\p{ID_Start}][$\p{ID_Continue}]*/u (исключая последовательности Unicode-экранирования).

Кроме того, JavaScript разрешает использовать последовательности Unicode-экранирования в форме \u0000 или \u{000000} в идентификаторах, которые кодируют то же строковое значение, что и фактические символы Unicode. Например, 你好 и \u4f60\u597d — это одни и те же идентификаторы:

const 你好 = "Hello";
console.log(\u4f60\u597d); // Hello

Не все места принимают полный набор идентификаторов. Определенные синтаксисы, такие как объявления функций, выражения функций и объявления переменных, требуют использования имен идентификаторов, которые не являются зарезервированными словами.

function import() {} // Illegal: import is a reserved word.

Наиболее заметно, что частные элементы и свойства объектов разрешают зарезервированные слова.

const obj = { import: "value" }; // Legal despite `import` being reserved
class C {
  #import = "value";
}

Ключевые слова

Ключевые слова — это токены, которые выглядят как идентификаторы, но имеют специальное значение в JavaScript. Например, ключевое слово async перед объявлением функции указывает, что функция является асинхронной.

Некоторые ключевые слова являются зарезервированными, что означает, что они не могут использоваться как идентификаторы для объявлений переменных, функций и т. д. Их часто называют зарезервированными словами. Список этих зарезервированных слов приведен ниже. Не все ключевые слова зарезервированы — например, async можно использовать как идентификатор где угодно. Некоторые ключевые слова являются контекстно зарезервированными — например, await зарезервировано только в теле асинхронной функции, а let зарезервировано только в коде строгого режима, или в объявлениях const и let.

Идентификаторы всегда сравниваются по строковому значению, поэтому последовательности экранирования интерпретируются. Например, это все еще синтаксическая ошибка:

const els\u{65} = 1;
// `els\u{65}` encodes the same identifier as `else`

Зарезервированные слова

Эти ключевые слова не могут использоваться как идентификаторы для переменных, функций, классов и т. д. где-либо в исходном коде JavaScript.

  • break
  • case
  • catch
  • class
  • const
  • continue
  • debugger
  • default
  • delete
  • do
  • else
  • export
  • extends
  • false
  • finally
  • for
  • function
  • if
  • import
  • in
  • instanceof
  • new
  • null
  • return
  • super
  • switch
  • this
  • throw
  • true
  • try
  • typeof
  • var
  • void
  • while
  • with

Следующие зарезервированы только в строгом режиме:

  • let (также зарезервировано в объявлениях const, let и классов)
  • static
  • yield (также зарезервировано в телах генераторных функций)

Следующие зарезервированы только в модульном коде или телах асинхронных функций:

  • await

Будущие зарезервированные слова

Следующие слова зарезервированы как будущие ключевые слова спецификацией ECMAScript. В настоящее время они не имеют специальной функциональности, но могут иметь ее в будущем, поэтому их нельзя использовать в качестве идентификаторов.

Эти слова зарезервированы всегда:

  • enum

Следующие слова зарезервированы только в строгом режиме:

  • implements
  • interface
  • package
  • private
  • protected
  • public

Будущие зарезервированные слова в старых стандартах

Следующие слова зарезервированы как будущие ключевые слова старыми спецификациями ECMAScript (ECMAScript 1-3).

  • abstract
  • boolean
  • byte
  • char
  • double
  • final
  • float
  • goto
  • int
  • long
  • native
  • short
  • synchronized
  • throws
  • transient
  • volatile

Идентификаторы с особым значением

Несколько идентификаторов имеют особое значение в некоторых контекстах, не являясь при этом зарезервированными словами. К ним относятся:

  • arguments (не ключевое слово, но не может быть объявлено как идентификатор в строгом режиме)
  • as (import * as ns from "mod")
  • async
  • eval (не ключевое слово, но не может быть объявлено как идентификатор в строгом режиме)
  • from (import x from "mod")
  • get
  • of
  • set

Литералы

Примечание: В этом разделе обсуждаются литералы, которые являются атомарными токенами. Литералы объектов и литералы массивов являются выражениями, состоящими из последовательности токенов.

Нулевой литерал

См. также null для получения дополнительной информации.

null

Логический литерал

См. также тип boolean для получения дополнительной информации.

true
false

Числовые литералы

Типы Number и BigInt используют числовые литералы.

Десятичный

1234567890
42

Десятичные литералы могут начинаться с нуля (0), за которым следует другая десятичная цифра, но если все цифры после начального 0 меньше 8, число интерпретируется как восьмеричное. Это считается устаревшим синтаксисом, и числовые литералы, которым предшествует 0, независимо от того, интерпретируются они как восьмеричные или десятичные, вызывают синтаксическую ошибку в строгом режиме — поэтому используйте префикс 0o.

0888 // 888 parsed as decimal
0777 // parsed as octal, 511 in decimal
Экспоненциальный

Десятичный экспоненциальный литерал определяется следующим форматом: beN; где b — это базовое число (целое или дробное), за которым следует символ E или e (который служит разделителем или индикатором экспоненты) и N, который является экспонентой или степенью — целым числом со знаком.

0e-5   // 0
0e+5   // 0
5e1    // 50
175e-2 // 1.75
1e3    // 1000
1e-3   // 0.001
1E3    // 1000

Двоичный

Синтаксис двоичных чисел использует ведущий ноль, за которым следует строчная или заглавная латинская буква "B" (0b или 0B). Любой символ после 0b, который не является 0 или 1, завершит последовательность литерала.

0b10000000000000000000000000000000 // 2147483648
0b01111111100000000000000000000000 // 2139095040
0B00000000011111111111111111111111 // 8388607

Восьмеричный

Синтаксис восьмеричных чисел использует ведущий ноль, за которым следует строчная или заглавная латинская буква "O" (0o или 0O). Любой символ после 0o, который находится вне диапазона (01234567), завершит последовательность литерала.

0O755 // 493
0o644 // 420

Шестнадцатеричный

Синтаксис шестнадцатеричных чисел использует ведущий ноль, за которым следует строчная или заглавная латинская буква "X" (0x или 0X). Любой символ после 0x, который находится вне диапазона (0123456789ABCDEF), завершит последовательность литерала.

0xFFFFFFFFFFFFF // 4503599627370495
0xabcdef123456  // 188900967593046
0XA             // 10

Литерал BigInt

Тип BigInt — это числовой примитив в JavaScript, который может представлять целые числа произвольной точности. Литералы BigInt создаются путем добавления n в конец целого числа.

123456789123456789n     // 123456789123456789
0o777777777777n         // 68719476735
0x123456789ABCDEFn      // 81985529216486895
0b11101001010101010101n // 955733

Литералы BigInt не могут начинаться с 0, чтобы избежать путаницы с устаревшими восьмеричными литералами.

0755n; // SyntaxError: invalid BigInt syntax

Для восьмеричных BigInt чисел всегда используйте ноль, за которым следует буква "o" (заглавная или строчная):

0o755n;

Для получения дополнительной информации о BigInt см. также структуры данных JavaScript.

Числовые разделители

Для улучшения читаемости числовых литералов подчеркивания (_, U+005F) могут использоваться в качестве разделителей:

1_000_000_000_000
1_050.95
0b1010_0001_1000_0101
0o2_2_5_6
0xA0_B0_C0
1_000_000_000_000_000_000_000n

Обратите внимание на следующие ограничения:

// More than one underscore in a row is not allowed
100__000; // SyntaxError

// Not allowed at the end of numeric literals
100_; // SyntaxError

// Can not be used after leading 0
0_1; // SyntaxError

Строковые литералы

Строковый литерал — это ноль или более кодовых точек Unicode, заключенных в одинарные или двойные кавычки. Кодовые точки Unicode также могут быть представлены в виде экранирующей последовательности. Все кодовые точки могут появляться в строковом литерале буквально, за исключением следующих кодовых точек:

  • U+005C \ (обратная косая черта)
  • U+000D <CR>
  • U+000A <LF>
  • Тот же тип кавычек, которым начинается строковый литерал

Любые кодовые точки могут присутствовать в виде экранирующей последовательности. Строковые литералы оцениваются как строковые значения ECMAScript. При генерации этих строковых значений кодовые точки Unicode кодируются в UTF-16.

'foo'
"bar"

В следующих подразделах описаны различные экранирующие последовательности (\, за которой следуют один или несколько символов), доступные в строковых литералах. Любая не перечисленная ниже экранирующая последовательность становится "экранированием идентичности", которое преобразуется в саму кодовую точку. Например, \z эквивалентно z. Существует устаревший синтаксис восьмеричных экранирований, описанный на странице Устаревшие и вышедшие из употребления возможности. Многие из этих экранирующих последовательностей также допустимы в регулярных выражениях — см. Экранирование символов.

Экранирующие последовательности

Специальные символы могут быть закодированы с использованием экранирующих последовательностей:

Экранирующая последовательность Кодовая точка Unicode
\0 нулевой символ (U+0000 NULL)
\' одиночная кавычка (U+0027 APOSTROPHE)
\" двойная кавычка (U+0022 QUOTATION MARK)
\\ обратная косая черта (U+005C REVERSE SOLIDUS)
\n новая строка (U+000A LINE FEED; LF)
\r возврат каретки (U+000D CARRIAGE RETURN; CR)
\v вертикальная табуляция (U+000B LINE TABULATION)
\t горизонтальная табуляция (U+0009 CHARACTER TABULATION)
\b забой (U+0008 BACKSPACE)
\f перевод страницы (U+000C FORM FEED)
\, за которым следует терминатор строки пустая строка

Последняя экранирующая последовательность, \, за которой следует терминатор строки, полезна для разделения строкового литерала на несколько строк без изменения его смысла.

const longString =
  "This is a very long string which needs \
to wrap across multiple lines because \
otherwise my code is unreadable.";

Убедитесь, что после обратной косой черты нет пробела или какого-либо другого символа (кроме переноса строки), иначе это не сработает. Если следующая строка имеет отступ, дополнительные пробелы также будут присутствовать в значении строки.

Вы также можете использовать оператор + для объединения нескольких строк, вот так:

const longString =
  "This is a very long string which needs " +
  "to wrap across multiple lines because " +
  "otherwise my code is unreadable.";

Оба приведенных выше метода дают идентичные строки.

Шестнадцатеричные экранирующие последовательности

Шестнадцатеричные экранирующие последовательности состоят из \x, за которым следуют ровно две шестнадцатеричные цифры, представляющие кодовый элемент или кодовую точку в диапазоне от 0x0000 до 0x00FF.

"\xA9"; // "©"

Unicode экранирующие последовательности

Unicode экранирующая последовательность состоит из ровно четырех шестнадцатеричных цифр после \u. Она представляет кодовый элемент в кодировке UTF-16. Для кодовых точек от U+0000 до U+FFFF кодовый элемент равен кодовой точке. Кодовые точки от U+10000 до U+10FFFF требуют двух экранирующих последовательностей, представляющих два кодовых элемента (суррогатную пару), используемых для кодирования символа; суррогатная пара отличается от кодовой точки.

См. также String.fromCharCode() и String.prototype.charCodeAt().

"\u00A9"; // "©" (U+A9)

Экранирование кодовых точек Unicode

Экранирование кодовой точки Unicode состоит из \u{, за которым следует кодовая точка в шестнадцатеричном представлении, а затем }. Значение шестнадцатеричных цифр должно находиться в диапазоне от 0 до 0x10FFFF включительно. Кодовые точки в диапазоне от U+10000 до U+10FFFF не требуют представления в виде суррогатной пары.

См. также String.fromCodePoint() и String.prototype.codePointAt().

"\u{2F804}"; // CJK COMPATIBILITY IDEOGRAPH-2F804 (U+2F804)

// the same character represented as a surrogate pair
"\uD87E\uDC04";

Литералы регулярных выражений

Литералы регулярных выражений заключены в две косые черты (/). Лексер поглощает все символы до следующей незащищенной косой черты или до конца строки, если только косая черта не появляется внутри класса символов ([]). Некоторые символы (а именно, те, которые являются частями идентификатора) могут появляться после закрывающей косой черты, обозначая флаги.

Лексическая грамматика очень снисходительна: не все литералы регулярных выражений, которые идентифицируются как один токен, являются допустимыми регулярными выражениями.

См. также RegExp для получения дополнительной информации.

/ab+c/g;
/[/]/;

Литерал регулярного выражения не может начинаться с двух косых черт (//), так как это был бы однострочный комментарий. Чтобы указать пустое регулярное выражение, используйте /(?:)/.

Шаблонные литералы

Один шаблонный литерал состоит из нескольких токенов: `xxx${ (шаблонная голова), }xxx${ (шаблонная середина) и }xxx` (шаблонный хвост) являются отдельными токенами, тогда как между ними может находиться любое выражение.

См. также шаблонные литералы для получения дополнительной информации.

`string text`;

`string text line 1
 string text line 2`;

`string text ${expression} string text`;

tag`string text ${expression} string text`;

Автоматическая вставка точки с запятой

Синтаксис некоторых инструкций JavaScript требует точки с запятой (;) в конце. К ним относятся:

  • var, let, const, using, await using
  • Инструкции выражений (Expression statements)
  • do...while
  • continue, break, return, throw
  • debugger
  • Объявления полей класса (публичные (public) или приватные (private))
  • import, export

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

1. Когда встречается токен, недопустимый по грамматике, и он отделен от предыдущего токена по крайней мере одним терминатором строки (включая блочный комментарий, содержащий по крайней мере один терминатор строки), или токен является "}", то перед токеном вставляется точка с запятой.

{ 1
2 } 3

// is transformed by ASI into:

{ 1
;2 ;} 3;

// Which is valid grammar encoding three statements,
// each consisting of a number literal

Конечная ")" в do...while также обрабатывается как особый случай этим правилом.

do {
  // …
} while (condition) /* ; */ // ASI here
const a = 1

Однако точки с запятой не вставляются, если бы точка с запятой стала разделителем в заголовке инструкции for.

for (
  let a = 1 // No ASI here
  a < 10 // No ASI here
  a++
) {}

Точки с запятой также никогда не вставляются в качестве пустых операторов. Например, в приведенном ниже коде, если после ")" вставить точку с запятой, код будет действительным, с пустым оператором в качестве тела if, а объявление const будет отдельным оператором. Однако, поскольку автоматически вставляемые точки с запятой не могут стать пустыми операторами, это приводит к тому, что объявление становится телом оператора if, что недопустимо.

if (Math.random() > 0.5)
const x = 1 // SyntaxError: Unexpected token 'const'

2. Когда достигнут конец потока входных токенов, и парсер не может разобрать единственный входной поток как полную программу, в конце вставляется точка с запятой.

const a = 1 /* ; */ // ASI here

Это правило является дополнением к предыдущему правилу, специально для случая, когда нет "оскорбительного токена", но есть конец входного потока.

3. Когда грамматика запрещает терминаторы строк в некотором месте, но терминатор строки найден, вставляется точка с запятой. Эти места включают:

  • expr <here> ++, expr <here> --
  • continue <here> lbl
  • break <here> lbl
  • return <here> expr
  • throw <here> expr
  • yield <here> expr
  • yield <here> * expr
  • (param) <here> => {}
  • async <here> function, async <here> prop(), async <here> function*, async <here> *prop(), async <here> (param) <here> => {}
  • using <here> id, await <here> using <here> id

Здесь ++ не рассматривается как постфиксный оператор, применяемый к переменной b, поскольку между b и ++ находится терминатор строки.

a = b
++c

// is transformed by ASI into

a = b;
++c;

Здесь оператор return возвращает undefined, а a + b становится недостижимым оператором.

return
a + b

// is transformed by ASI into

return;
a + b;

Обратите внимание, что ASI срабатывает только в том случае, если разрыв строки разделяет токены, которые в противном случае привели бы к недопустимому синтаксису. Если следующий токен может быть разобран как часть допустимой структуры, точки с запятой не будут вставлены. Например:

const a = 1
(1).toString()

const b = 1
[1, 2, 3].forEach(console.log)

Поскольку () может рассматриваться как вызов функции, он обычно не вызывает ASI. Аналогично, [] может быть доступом к члену. Приведенный выше код эквивалентен:

const a = 1(1).toString();

const b = 1[1, 2, 3].forEach(console.log);

Это оказывается допустимым синтаксисом. 1[1, 2, 3] является аксессором свойства с выражением, объединенным запятыми. Следовательно, вы получите ошибки, такие как "1 is not a function" и "Cannot read properties of undefined (reading 'forEach')", при запуске кода.

Внутри классов поля классов и генераторные методы также могут быть подводным камнем.

class A {
  a = 1
  *gen() {}
}

Это рассматривается как:

class A {
  a = 1 * gen() {}
}

И поэтому вызовет синтаксическую ошибку в районе {.

Существуют следующие эмпирические правила для работы с ASI, если вы хотите принудительно использовать стиль без точек с запятой:

  • Записывайте постфиксные ++ и -- на той же строке, что и их операнды.

    const a = b
    ++
    console.log(a) // ReferenceError: Invalid left-hand side expression in prefix operation
    
    const a = b++
    console.log(a)
    
  • Выражения после return, throw или yield должны быть на той же строке, что и ключевое слово.

    function foo() {
      return
        1 + 1 // Returns undefined; 1 + 1 is ignored
    }
    
    function foo() {
      return 1 + 1
    }
    
    function foo() {
      return (
        1 + 1
      )
    }
    
  • Аналогично, идентификатор метки после break или continue должен быть на той же строке, что и ключевое слово.

    outerBlock: {
      innerBlock: {
        break
          outerBlock // SyntaxError: Illegal break statement
      }
    }
    
    outerBlock: {
      innerBlock: {
        break outerBlock
      }
    }
    
  • => стрелочной функции должен быть на той же строке, что и конец ее параметров.

    const foo = (a, b)
      => a + b
    
    const foo = (a, b) =>
      a + b
    
  • async асинхронных функций, методов и т.д. не может непосредственно следовать за терминатором строки.

    async
    function foo() {}
    
    async function
    foo() {}
    
  • Ключевое слово using в инструкциях using и await using должно быть на той же строке, что и первый объявленный им идентификатор.

    using
    resource = acquireResource()
    
    using resource
      = acquireResource()
    
  • Если строка начинается с одного из (, [, `, +, -, / (как в регулярных выражениях), предшествуйте ей точкой с запятой или завершите предыдущую строку точкой с запятой.

    // The () may be merged with the previous line as a function call
    (() => {
      // …
    })()
    
    // The [ may be merged with the previous line as a property access
    [1, 2, 3].forEach(console.log)
    
    // The ` may be merged with the previous line as a tagged template literal
    `string text ${data}`.match(pattern).forEach(console.log)
    
    // The + may be merged with the previous line as a binary + expression
    +a.toString()
    
    // The - may be merged with the previous line as a binary - expression
    -a.toString()
    
    // The / may be merged with the previous line as a division expression
    /pattern/.exec(str).forEach(console.log)
    
    ;(() => {
      // …
    })()
    ;[1, 2, 3].forEach(console.log)
    ;`string text ${data}`.match(pattern).forEach(console.log)
    ;+a.toString()
    ;-a.toString()
    ;/pattern/.exec(str).forEach(console.log)
    
  • Поля классов предпочтительно всегда должны заканчиваться точкой с запятой — в дополнение к предыдущему правилу (которое включает объявление поля, за которым следует вычисляемое свойство, поскольку последнее начинается с [), точки с запятой также требуются между объявлением поля и генераторным методом.

    class A {
      a = 1
      [b] = 2
      *gen() {} // Seen as a = 1[b] = 2 * gen() {}
    }
    
    class A {
      a = 1;
      [b] = 2;
      *gen() {}
    }
    

Спецификации

Спецификация
Спецификация языка ECMAScript® 2027

Совместимость с браузерами

Desktop Mobile Server
Chrome Edge Firefox Opera Safari Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet WebView Android WebView on iOS Bun Deno Node.js
array_literals
1
12
1
4
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
binary_numeric_literals
41
12
25
28
9
41
25
28
9
4.0
41
9
1.0.0
1.0
4.0.0
boolean_literals
1
12
1
3
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
decimal_numeric_literals
1
12
1
3
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
hashbang_comments
74
79
67
62
13.1
74
67
53
13.4
11.0
74
13.4
1.0.0
1.0
0.10.0
hexadecimal_escape_sequences
1
12
1
4
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
hexadecimal_numeric_literals
1
12
1
3
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
null_literal
1
12
1
3
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
numeric_separators
75
79
70
62
13
75
79
54
13
11.0
75
13
1.0.0
1.2
12.5.0
octal_numeric_literals
41
12
25
28
9
41
25
28
9
4.0
41
9
1.0.0
1.0
4.0.0
regular_expression_literals
1
12
1
5
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
shorthand_object_literals
43
12
33
30
9
43
33
30
9
4.0
43
9
1.0.0
1.0
4.0.0
string_literals
1
12
1
3
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
template_literals
41
12
34
28
9
41
34
28
9
4.0
41
9
1.0.0
1.0
4.0.0
trailing_commas
1
12
1
9.5
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
unicode_escape_sequences
1
12
1
4
1
18
4
10.1
1
1.0
4.4
1
1.0.0
1.0
0.10.0
unicode_point_escapes
44
12
40
31
9
44
40
32
9
4.0
44
9
1.0.0
1.0
4.0.0

См. также

  • Руководство по грамматике и типам
  • Микро-функция из ES6, теперь в Firefox Aurora и Nightly: двоичные и восьмеричные числа от Jeff Walden (2013)
  • Последовательности экранирования символов JavaScript от Mathias Bynens (2011)

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API