Лексическая грамматика
На этой странице описывается лексическая грамматика 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.
breakcasecatchclassconstcontinuedebuggerdefaultdeletedoelseexportextendsfalsefinallyforfunctionifimportininstanceofnewnullreturnsuperswitchthisthrowtruetrytypeofvarvoidwhilewith
Следующие зарезервированы только в строгом режиме:
-
let(также зарезервировано в объявленияхconst,letи классов) static-
yield(также зарезервировано в телах генераторных функций)
Следующие зарезервированы только в модульном коде или телах асинхронных функций:
Будущие зарезервированные слова
Следующие слова зарезервированы как будущие ключевые слова спецификацией ECMAScript. В настоящее время они не имеют специальной функциональности, но могут иметь ее в будущем, поэтому их нельзя использовать в качестве идентификаторов.
Эти слова зарезервированы всегда:
enum
Следующие слова зарезервированы только в строгом режиме:
implementsinterfacepackageprivateprotectedpublic
Будущие зарезервированные слова в старых стандартах
Следующие слова зарезервированы как будущие ключевые слова старыми спецификациями ECMAScript (ECMAScript 1-3).
abstractbooleanbytechardoublefinalfloatgotointlongnativeshortsynchronizedthrowstransientvolatile
Идентификаторы с особым значением
Несколько идентификаторов имеют особое значение в некоторых контекстах, не являясь при этом зарезервированными словами. К ним относятся:
-
arguments(не ключевое слово, но не может быть объявлено как идентификатор в строгом режиме) -
as(import * as ns from "mod") async-
eval(не ключевое слово, но не может быть объявлено как идентификатор в строгом режиме) -
from(import x from "mod") getofset
Литералы
Примечание: В этом разделе обсуждаются литералы, которые являются атомарными токенами. Литералы объектов и литералы массивов являются выражениями, состоящими из последовательности токенов.
Нулевой литерал
См. также 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> lblbreak <here> lblreturn <here> exprthrow <here> expryield <here> expryield <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