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

Ця сторінка описує лексичну граматику JavaScript. Текст коду на JavaScript – лише послідовність символів, – аби інтерпретатор зрозумів його, рядок повинен бути розібраний до більш структурованого представлення. Початковий етап розбору зветься лексичним аналізом, при якому текст оглядається зліва направо й перетворюється на послідовність окремих, неподільних елементів введення. Частина елементів введення неважлива для інтерпретатора, тож може бути прибрана після цього кроку – серед них пробіли та коментарі. Решта, в тому числі ідентифікатори, ключові слова, літерали та розділові знаки (здебільшого оператори), використовуються при подальшому синтаксичному аналізі. Символи кінця рядка й багаторядкові коментарі також є синтаксично неважливими, але вони допоміжні для автоматичного вставляння крапок з комою, завдяки якому певні недійсні послідовності лексем стають дійсними.

Символи контролю форматування

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

Кодова точка Назва Абревіатура Опис
U+200C Нез'єднувач нульової ширини <ZWNJ> Ставиться між символами, аби запобігти їх поєднанню в лігатури, в деяких мовах (Wikipedia).
U+200D З'єднувач нульової ширини <ZWJ> Ставиться між символами, котрі зазвичай не з'єднуються, аби вони були візуалізовані за допомогою їхньої об'єднаної форми, в деяких мовах (Wikipedia).
U+FEFF Маркер порядку байтів <BOM> Використовується на початку сценарію, аби позначити його як Unicode, а також порядок байтів у тексті (Вікіпедія).

У тексті коду мовою 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> Вікіпедія
U+2029 Розділювач абзаців <PS> Вікіпедія

Коментарі

Коментарі використовуються для додавання підказок, нотаток, пропозицій чи застережень до коду JavaScript. Вони можуть робити цей код легшим для прочитання та розуміння. Вони можуть використовуватись для вимикання коду та запобігання його виконанню; це може бути цінним інструментом зневадження.

JavaScript має два традиційні способи додавання коментарів до коду: рядкові коментарі та блокові коментарі. на додачу, існує особливий синтаксис шебанг-коментарів.

Рядкові коментарі

Перший спосіб – коментар //; це робить увесь текст, що стоїть далі до кінця рядка, коментарем. Наприклад:

function comment() {
  // Це однорядковий коментар JavaScript
  console.log("Привіт, світе!");
}
comment();

Блокові коментарі

Другий спосіб – стиль /* */, котрий є гнучкішим.

Наприклад, можна використати його на одному рядку:

function comment() {
  /* Це однорядковий коментар JavaScript */
  console.log("Привіт, світе!");
}
comment();

Можна також робити багаторядкові коментарі, отак:

function comment() {
  /* Цей коментар стоїть на кількох рядках. Зверніть увагу,
     що немає потреби закінчувати коментар, поки не хочеться. */
  console.log("Привіт, світе!");
}
comment();

Також його можна застосовувати посередині рядка, якщо є потреба, хоч це й робить код важчим для прочитання, тож так слід робити з обережністю:

function comment(x) {
  console.log("Привіт, " + x /* вставлення значення x */ + " !");
}
comment("світе");

На додачу, це можна використовувати для вимикання коду, шляхом загортання його в коментар, отак:

function comment() {
  /* console.log("Привіт, світе!"); */
}
comment();

У цьому випадку, виклик console.log() ніколи не відбудеться, адже він знаходиться всередині коментаря. Так можна вимкнути будь-яку кількість рядків коду.

Блокові коментарі, котрі містять щонайменше один символ кінця рядка, поводяться при автоматичному вставлянні крапок з комою як символи кінця рядка.

Шебанг-коментарі

Є особливий третій синтаксис коментарів, шебанг-коментар. Шебанг-коментар поводиться точно так само, як однорядковий (//) коментар, окрім того, що він починається з #! і є дійсним лише на абсолютному початку сценарію або модуля. Зверніть увагу, що перед #! не може стояти жодних пробільних символів. Такий коментар складається з усіх символів після #! і до кінця першого рядка; дозволений лише один такий коментар.

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

#!/usr/bin/env node

console.log("Привіт, світе");

Інтерпретатор JavaScript розглядатиме це як звичайний коментар: він має значення лише для оболонки, якщо сценарій запускається в ній безпосередньо.

Застереження: Аби мати змогу запускати сценарії в середовищі оболонки, їх слід кодувати в UTF-8 без BOM. Попри те, що BOM не призводить до жодних проблем для коду, що працює в браузері – бо цей символ прибирається під час розкодування UTF-8, до аналізу тексту коду – оболонка Unix чи Linux не впізнає шебанг, якщо перед ним стоятиме символ BOM.

Коментар у стилі #! повинен використовуватися лише для задання інтерпретатора JavaScript. У всіх інших випадках повинні використовуватись прості коментарі // (або ж багаторядкові).

Ідентифікатори

Ідентифікатор використовується для зв'язування значення з іменем. Ідентифікатори можуть вживатися в різних місцях:

const decl = 1; // Оголошення змінних (може також бути з `let` або `var`)
function fn() {} // Оголошення функцій
const obj = { key: "value" }; // Ключі об'єктів
// Оголошення класів
class C {
  #priv = "value"; // Приватні властивості
}
lbl: console.log(1); // Підписи

У JavaScript ідентифікатори здебільшого складаються з алфавітно-цифрових символів, підкреслень (_) і знаків долара ($). Вони не можуть починатися з цифр. Проте ідентифікатори JavaScript не обмежені ASCII: так само дозволені чимало кодових точок Unicode. А саме, усі символи категорії ID_Start можуть стояти на початку ідентифікатора, а всі символи категорії ID_Continue можуть зустрічатися після першого символу.

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

На додачу до цього, JavaScript дозволяє використання в ідентифікаторах екрановані послідовності Unicode, у вигляді \u0000 або \u{000000}, що кодує ті самі рядкові значення, що й фактичні символи Unicode. Наприклад, 你好 і \u4f60\u597d – це один і той же ідентифікатор:

const 你好 = "Привіт";
console.log(\u4f60\u597d); // Привіт

Не у всіх місцях приймається повний діапазон ідентифікаторів. Частина синтаксичних конструкцій, як то: оголошення функцій, вирази функцій та оголошення змінних — вимагають використання ідентифікаторів, що не є зарезервованими словами.

function import() {} // Заборонено: import – зарезервоване слово.

Слід зазначити, що приватні властивості та властивості об'єктів – дозволяють зарезервовані слова.

const obj = { import: "value" }; // Дозволено, попри те, що слово `import` – зарезервоване
class C {
  #import = "value";
}

Ключові слова

Ключові слова – лексеми, котрі на вигляд подібні до ідентифікаторів, але мають в JavaScript особливе значення. Наприклад, ключове слово async перед оголошенням функції позначає, що ця функція – асинхронна.

Частина ключових слів – зарезервовані, тобто їх не можна використовувати як ідентифікатори при оголошенні змінних, функцій тощо. Їх нерідко звуть зарезервованими словами. Список цих зарезервованих слів – наведений нижче. Не всі ключові слова – зарезервовані: наприклад, async може використовуватись як ідентифікатор де завгодно. Частина ключових слів є зарезервованими контекстно: наприклад, await – зарезервоване лише всередині тіла асинхронної функції, а let – зарезервоване лише в коді у суворому режимі, або ж оголошеннях const і let.

Ідентифікатори завжди перевіряються за їх рядковим значенням, тож послідовності екранування в них – інтерпретуються. Наприклад, код нижче – все одно є синтаксичною помилкою:

const els\u{65} = 1;
// `els\u{65}` кодує той самий ідентифікатор, що й `else`

Зарезервовані слова

Ці ключові слова не можуть вживатися як ідентифікатори для змінних, функцій, класів тощо, будь-де в коді на JavaScript.

Наступні слова вважаються зарезервованими лише тоді, коли зустрічаються в коді в суворому режимі:

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

Наступне слово вважається зарезервованим лише тоді, коли зустрічається в модульному коді або тілі асинхронної функції:

Зарезервовані на майбутнє слова

Слова нижче – зарезервовані специфікацією 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.

null

Булів літерал

Докладніше дивіться на сторінці булевого типу.

true
false

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

Числові літерали використовують типи Number і BigInt.

Десяткові числа

1234567890
42

Десяткові літерали можуть починатися з нуля (0), після якого стоїть будь-яка інша десяткова цифра, але якщо всі цифри після 0 на початку менші за 8 – то число тлумачиться як вісімкове. Це вважається історичним синтаксисом, і числові літерали, що починаються з 0, незалежно від того, чи тлумачаться вони як вісімкові, чи як десяткові, призводять до синтаксичної помилки в суворому режимі – таким чином, натомість слід використовувати префікс 0o.

0888 // 888 розбирається як десяткове
0777 // розбирається як вісімкове, тобто дає десяткове 511
Степеневий запис

Десятковий степеневий літерал – заданий наступним форматом: 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), завершить послідовність літерала.

0xFFFFFFFFFFFFFFFFF // 295147905179352830000
0x123456789ABCDEF   // 81985529216486900
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

Зверніть увагу на наступні обмеження:

// Не можна ставити більш ніж одне підкреслення підряд
100__000; // SyntaxError

// Не можна ставити підкреслення в кінці числового літерала
100_; // SyntaxError

// Не можна ставити підкреслення після 0 на початку
0_1; // SyntaxError

Рядкові літерали

Рядковий літерал – це нуль або більше кодових точок Unicode, загорнутих в одинарні чи подвійні лапки. Також кодові точки Unicode можуть бути представлені екранованою послідовністю. В рядкових літералах можуть зустрічатися в буквальному вигляді усі кодові точки, окрім оцих кодових точок:

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

Усі кодові точки можуть зустрічатися у вигляді послідовності екранування. Рядкові літерали обчислюються до рядкових значень ECMAScript. При генеруванні цих рядкових значень кодові точки Unicode кодуються в UTF-16.

'foo'
"bar"

Наступні підрозділи описують різні послідовності екранування (символ \, за яким слідує один або більше символів), доступні у рядкових літералах. Будь-яка послідовність екранування, що не вказана нижче, стає "екрануванням ідентичності", що стає самою кодовою точкою. Наприклад, \z – це те саме, що й z. Існує нерекомендована синтаксична конструкція вісімкового екранування, описана на сторінці Нерекомендовані та застарілі можливості. Чимало з цих послідовностей екранування також є дійсними у регулярних виразах – див. Екранування символів.

Послідовності екранування

Спеціальні символи можуть бути закодовані за допомогою послідовностей екранування:

Послідовність екранування Кодова точка Unicode
\0 символ null (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 =
  "Це дуже довгий рядок, що потребує \
розкладання його на кількох рядах, \
бо інакше мій код – нечитабельний.";

Слід пересвідчитись, що після зворотної скісної риски немає ані пробілу, ані будь-якого іншого символу (окрім розриву рядка), бо інакше це не спрацює. Якщо наступний рядок має відступ, то додаткові пробіли також будуть присутні у значенні рядка.

Також можна використовувати оператор +, щоб з'єднати декілька рядків разом, як у прикладі нижче:

const longString =
  "Це дуже довгий рядок, що потребує " +
  "розкладання його на кількох рядах, " +
  "бо інакше мій код – нечитабельний.";

Обидва методи, наведені вище, дають однакові рядки.

Шістнадцяткові послідовності екранування

Шістнадцяткові послідовності екранування складаються з \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)

// той самий символ у вигляді сурогатної пари
"\uD87E\uDC04";

Літерали регулярних виразів

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

Лексична граматика JavaScript - вельми поблажлива: не всі літерали регулярних виразів, котрі впізнаються як єдиний токен, є дійсними регулярними виразами.

Дивіться докладніше на сторінці 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 вимагає крапок з комою (;) на їх кінці. Серед таких інструкцій:

Проте для того, щоб мова JavaScript була доступнішою та зрозумілішою, JavaScript автоматично вставляє крапки з комою, коли захоплює потік лексем, тож частина недійсних послідовностей лексем може бути "виправлена" до дійсного синтаксису. Цей крок відбувається після того, як текст програми розбирається на лексеми згідно з лексичною граматикою. Є три випадки, за яких автоматично вставляються крапки з комою:

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

{ 1
2 } 3

// автоматично трансформується на:

{ 1
;2 ;} 3;

// Це є дійсною граматикою, що кодує три інструкції,
// кожна з яких складається з числового літерала

Завершальна ")" циклу do...while також обробляється цим правилом – як особливий випадок.

do {
  // ...
} while (condition) /* ; */ // Автоматичне вставляння – тут
const a = 1

Проте крапка з комою не вставляється, якщо вона стала б розділювачем у заголовку інструкції for.

for (
  let a = 1 // Тут без автоматичного вставляння
  a < 10 // Тут без автоматичного вставляння
  a++
) {}

Крім цього, крапки з комою ніколи не вставляються як порожні інструкції. Наприклад, у коді нижче, якщо вставити крапку з комою після ")", то код був би дійсним, з порожньою інструкцією як тілом if і оголошенням const як окремою інструкцією. Проте завдяки тому, що автоматично вставлені крапки з комою не можуть ставати порожніми інструкціями, це призводить до того, що тілом інструкції if стає оголошення, що не є дійсним записом.

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

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

const a = 1 /* ; */ // Автоматичне вставляння тут

Це правило доповнює попереднє, а саме – для такого випадку, коли немає "лексеми-порушника", але настав кінець потоку введення.

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> => {}

Тут ++ не розглядається як постфіксний оператор, що застосовується до змінної b, тому що між b і ++ знаходиться символ кінця рядка.

a = b
++c

// автоматичним вставлянням трансформується до

a = b;
++c;

Тут інструкція return повертає undefined, і a + b стає недосяжною інструкцією.

return
a + b

// автоматичним вставлянням трансформується до

return;
a + b;

Зверніть увагу, що автоматичне вставляння запускається лише тоді, коли розрив рядка розділяє лексеми, що без нього утворюють недійсний запис. Якщо наступна лексема може бути розібрана як частина дійсної структури, то крапки з комою не вставляються. Наприклад:

const a = 1
(1).toString()

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

Через те, що () можуть розглядатися як виклик функції, зазвичай вони не спричиняють запуску автоматичного вставляння. Подібно до цього, [] можуть бути звертанням до елемента. Код вище – рівносильний щодо:

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

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

Так вийшло, що це дійсний синтаксис. 1[1, 2, 3] – це аксесор властивості з виразом, об'єднаним комою. Таким чином, при запуску коду вийдуть помилки виду "1 is not a function" and "Cannot read properties of undefined (reading 'forEach')".

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

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

Розглядається як:

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

І таким чином – буде синтаксична помилка біля {.

Є кілька емпіричних правил для роботи з автоматичним вставлянням, коли є потреба примушувати до стилю без крапок з комою:

  • Постфіксні ++ і -- слід писати на тому ж рядку, що і їхні операнди.

    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 // Повертає undefined; 1 + 1 – ігнорується
    }
    
    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() {}
    
  • Якщо рядок починається з (, [, `, +, - або / (як в літералах регулярних виразів), слід поставити перед цим крапку з комою, або ж поставити в кінець попереднього рядка крапку з комою.

    // () може зливатися з попереднім рядком як частина виклику функції
    (() => {
      // ...
    })()
    // [ може зливатися з попереднім рядком як частина звертання до властивості
    [1, 2, 3].forEach(console.log)
    // ` може зливатися з попереднім рядком як частина тегованого шаблонного літерала
    `string text ${data}`.match(pattern).forEach(console.log)
    // + може зливатися з попереднім рядком як частина бінарного виразу +
    +a.toString()
    // - може зливатися з попереднім рядком як частина бінарного виразу -
    -a.toString()
    // / може зливатися з попереднім рядком як частина виразу ділення
    /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() {} // Розглядається як a = 1[b] = 2 * gen() {}
    }
    
    class A {
      a = 1;
      [b] = 2;
      *gen() {}
    }
    

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

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
Array literals ([1, 2, 3]) Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 4
Opera Full support 4
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Binary numeric literals (0b) Chrome Full support 41
Edge Full support 12
Firefox Full support 25
Internet Explorer No support No
Opera Full support 28
Safari Full support 9
WebView Android Full support 41
Chrome Android Full support 41
Firefox for Android Full support 25
Opera Android Full support 28
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 4.0.0
Boolean literals (true/false) Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 3
Opera Full support 3
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Decimal numeric literals (1234567890) Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 3
Opera Full support 3
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Hashbang (#!) comment syntax Chrome Full support 74
Edge Full support 79
Firefox Full support 67
Internet Explorer No support No
Opera Full support 62
Safari Full support 13.1
WebView Android Full support 74
Chrome Android Full support 74
Firefox for Android Full support 67
Opera Android Full support 53
Safari on iOS Full support 13.4
Samsung Internet Full support 11.0
Deno Full support 1.0
Node.js Full support 0.10.0
Hexadecimal escape sequences (\'\xA9\') Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 4
Opera Full support 4
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Hexadecimal numeric literals (0xAF) Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 3
Opera Full support 3
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Null literal (null) Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 3
Opera Full support 3
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Numeric separators (1_000_000_000_000) Chrome Full support 75
Edge Full support 79
Firefox Full support 70
Internet Explorer No support No
Opera Full support 62
Safari Full support 13
WebView Android Full support 75
Chrome Android Full support 75
Firefox for Android Full support 79
Opera Android No support No
Safari on iOS Full support 13
Samsung Internet Full support 11.0
Deno Full support 1.2
Node.js Full support 12.5.0
Octal numeric literals (0o) Chrome Full support 41
Edge Full support 12
Firefox Full support 25
Internet Explorer No support No
Opera Full support 28
Safari Full support 9
WebView Android Full support 41
Chrome Android Full support 41
Firefox for Android Full support 25
Opera Android Full support 28
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 4.0.0
Regular expression literals (/ab+c/g) Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 4
Opera Full support 5
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Shorthand notation for object literals
Chrome Full support 43
Edge Full support 12
Firefox Full support 33
Internet Explorer No support No
Opera Full support 30
Safari Full support 9
WebView Android Full support 43
Chrome Android Full support 43
Firefox for Android Full support 33
Opera Android Full support 30
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 4.0.0
String literals (\'Hello world\') Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 3
Opera Full support 3
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Template literals Chrome Full support 41
Edge Full support 12
Firefox Full support 34
Internet Explorer No support No
Opera Full support 28
Safari Full support 9
WebView Android Full support 41
Chrome Android Full support 41
Firefox for Android Full support 34
Opera Android Full support 28
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 4.0.0
Escape sequences allowed in tagged template literals
Chrome Full support 62
Edge Full support 79
Firefox Full support 53
Internet Explorer No support No
Opera Full support 49
Safari Full support 11
WebView Android Full support 62
Chrome Android Full support 62
Firefox for Android Full support 53
Opera Android Full support 46
Safari on iOS Full support 11
Samsung Internet Full support 8.0
Deno Full support 1.0
Node.js Full support 8.10.0
Trailing commas Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 9
Opera Full support 9.5
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Trailing comma in function parameters
Chrome Full support 58
Edge Full support 14
Firefox Full support 52
Internet Explorer No support No
Opera Full support 45
Safari Full support 10
WebView Android Full support 58
Chrome Android Full support 58
Firefox for Android Full support 52
Opera Android Full support 43
Safari on iOS Full support 10
Samsung Internet Full support 7.0
Deno Full support 1.0
Node.js Full support 8.0.0
Trailing comma in object literals
Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 9
Opera Full support 9.5
Safari Full support 3
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Unicode escape sequences (\'\u00A9\') Chrome Full support 1
Edge Full support 12
Firefox Full support 1
Internet Explorer Full support 4
Opera Full support 4
Safari Full support 1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0
Unicode point escapes (\u{}) Chrome Full support 44
Edge Full support 12
Firefox Full support 40
Internet Explorer No support No
Opera Full support 31
Safari Full support 9
WebView Android Full support 44
Chrome Android Full support 44
Firefox for Android Full support 40
Opera Android Full support 32
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 4.0.0

Дивіться також