Лексична граматика
Ця сторінка описує лексичну граматику 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" |
[!NOTE] Серед тих символів, що мають властивість "White_Space", але не належать до загальної категорії "Space_Separator", U+0009, U+000B і U+000C у JavaScript все одно обробляються як пробіли; U+0085 NEXT LINE не має особливої ролі; решта – стають множиною символів кінця рядка.
[!NOTE] Зміни до стандарту 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 розглядатиме це як звичайний коментар: він має значення лише для оболонки, якщо сценарій запускається в ній безпосередньо.
[!WARNING] Аби мати змогу запускати сценарії в середовищі оболонки, їх слід кодувати в 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 можуть зустрічатися після першого символу.
[!NOTE] Якщо, з якоїсь причини, є потреба самотужки розібрати якийсь код на 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.
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
(також зарезервоване в тілах генераторних функцій)
Наступне слово вважається зарезервованим лише тоді, коли зустрічається в модульному коді або тілі асинхронної функції:
Зарезервовані на майбутнє слова
Слова нижче – зарезервовані специфікацією 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
Літерали
[!NOTE] Цей розділ описує літерали, що є неподільними лексемами. Об'єктні літерали та літерали масивів є виразами, що складаються з низки лексем.
Літерал 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 вимагає крапок з комою (;
) на їх кінці. Серед таких інструкцій:
var
,let
,const
- Інструкції-вирази
do...while
continue
,break
,return
,throw
debugger
- Оголошення полів класів (публічних та приватних)
import
,export
Проте для того, щоб мова 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() {} }
Специфікації
Специфікація |
---|
ECMAScript Language Specification (ECMAScript) |
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 Ні | 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 |
Дивіться також
- Посібник Граматика та типи
- Мікроможливість з ES6, уже доступна в Firefox Aurora та Nightly, – двійкові та вісімкові числа від Джефа Волдена (2013)
- Послідовності екранування символів у JavaScript від Матіаса Байненса (2011)