Вирази та оператори
Цей розділ описує вирази та оператори JavaScript, включно з операторами присвоєння, порівняння, арифметичними, бітовими, логічними, рядковими, тернарним і так далі.
На високому рівні, вираз – це дійсна одиниця коду, що обчислюється до значення. Є два типи виразів: ті, котрі мають побічні ефекти (як то присвоєння значень), і ті, котрі суто обчислюються.
Вираз x = 7
є прикладом першого типу. Цей вираз вживає оператор =
для присвоєння змінній x
значення семи. Сам вираз обчислюється в 7
.
Вираз 3 + 4
є прикладом другого типу. Цей вираз вживає оператор +
для додавання 3
і 4
докупи й вироблення значення 7
. Проте якщо такий вираз не є частиною більшої конструкції (наприклад, оголошення змінної виду const z = 3 + 4
), то його результат буде негайно відкинутий; зазвичай це буває помилкою програміста, адже обчислення не виробляє жодних ефектів.
Як показують приклади вище, усі складні вирази об'єднуються операторами, як то =
і +
. У цій частині – знайомство з наступними операторами:
- Оператори присвоєння
- Оператори порівняння
- Арифметичні оператори
- Бітові оператори
- Логічні оператори
- Оператори BigInt
- Рядкові оператори
- Умовний (тернарний) оператор
- Оператор коми
- Унарні оператори
- Оператори відношення
Ці оператори об'єднують операнди, котрі або сформовані операторами вищого пріоритету, або одним із базових виразів. Повний і розгорнутий список операторів та виразів доступний також у довіднику.
Пріоритет операторів визначає порядок, в якому вони застосовуються при обчисленні виразу. Наприклад:
const x = 1 + 2 * 3;
const y = 2 * 3 + 1;
Попри те, що *
і +
зустрічаються в різному порядку, обидва вирази дадуть 7
, тому що *
має пріоритет над +
, тож поєднаний *
вираз завжди буде обчислений першим. Пріоритет операторів можна відкинути за допомогою дужок (що утворює згрупований вираз — один з базових виразів). Повну таблицю пріоритетів операторів, а також різні каверзи – шукайте на сторінці Довідки пріоритету операторів.
JavaScript має і бінарні, і унарні оператори, а також один особливий тернарний оператор – умовний оператор. Бінарний оператор вимагає двох операндів, одного перед оператором і одного після:
operand1 operator operand2
Наприклад, 3 + 4
або x * y
. Така форма зветься інфіксним бінарним оператором, тому що оператор розміщений між двома операндами. Усі бінарні оператори в JavaScript є інфіксними.
Унарні оператори вимагають одного операнда – або перед, або після оператора:
operator operand
operand operator
Наприклад, x++
або ++x
. Форма operator operand
зветься префіксним унарним оператором, а форма operand operator
– постфіксним унарним оператором. ++
і --
– єдині постфіксні оператори в JavaScript; уся решта операторів, як то !
, typeof
тощо, – є префіксними.
Оператори присвоєння
Оператор присвоєння присвоює значення своєму лівому операндові на основі значення правого операнда. Простий оператор присвоєння – це знак дорівнює(=
), він присвоює значення свого правого операнда лівому операндові.
Тобто x = f()
– це вираз присвоєння, котрий присвоює значення f()
змінній x
.
Крім нього, є складені оператори присвоєння, котрі є скороченнями операцій, перелічених у наступній таблиці:
Назва | Оператор-скорочення | Значення |
---|---|---|
Присвоєння | x = f() |
x = f() |
Присвоєння з додаванням | x += f() |
x = x + f() |
Присвоєння з відніманням | x -= f() |
x = x - f() |
Присвоєння з множенням | x *= f() |
x = x * f() |
Присвоєння з діленням | x /= f() |
x = x / f() |
Присвоєння зі взяттям остачі від ділення | x %= f() |
x = x % f() |
Присвоєння з піднесенням до степеня | x **= f() |
x = x ** f() |
Присвоєння зі зсувом уліво | x <<= f() |
x = x << f() |
Присвоєння зі зсувом управо | x >>= f() |
x = x >> f() |
Присвоєння з беззнаковим зсувом управо | x >>>= f() |
x = x >>> f() |
Присвоєння з бітовою кон'юнкцією | x &= f() |
x = x & f() |
Присвоєння з бітовою виключною диз'юнкцією | x ^= f() |
x = x ^ f() |
Присвоєння з бітовою диз'юнкцією | x |= f() |
x = x | f() |
Присвоєння з логічною кон'юнкцією | x &&= f() |
x && (x = f()) |
Присвоєння з логічною диз'юнкцією | x ||= f() |
x || (x = f()) |
Присвоєння з null-злиттям | x ??= f() |
x ?? (x = f()) |
Присвоєння властивостям
Якщо вираз обчислюється до об'єкта, то ліва сторона виразу присвоєння може виконувати присвоєння властивостям цього виразу. Наприклад:
const obj = {};
obj.x = 3;
console.log(obj.x); // Виводить 3.
console.log(obj); // Виводить { x: 3 }.
const key = "y";
obj[key] = 5;
console.log(obj[key]); // Виводить 5.
console.log(obj); // Виводить { x: 3, y: 5 }.
Більше інформації про об'єкти – в Роботі з об'єктами.
Якщо вираз не обчислюється до об'єкта, то присвоєння властивостям такого виразу – не виконуються.
const val = 0;
val.x = 3;
console.log(val.x); // Виводить undefined.
console.log(val); // Виводить 0.
У суворому режимі код вище викидає помилку, тому що не можна присвоювати властивості примітивам.
Є помилкою присвоєння значень незмінним властивостям або властивостям виразів без властивостей (null
і undefined
).
Деструктурування
Корисний для складних присвоєнь синтаксис присвоєння з деструктуруванням – це вираз JavaScript, котрий дає змогу видобути дані з масивів та об'єктів за допомогою синтаксису, котрий є дзеркальним відображенням конструювання масивів та об'єктних літералів.
Без деструктурування для видобуття з масивів і об'єктів кількох значень необхідні кілька інструкцій:
const foo = ["один", "два", "три"];
// без деструктурування
const one = foo[0];
const two = foo[1];
const three = foo[2];
Завдяки деструктуруванню можна видобувати кілька значень в окремі змінні за допомогою однієї інструкції:
const [one, two, three] = foo;
Обчислення та вкладеність
Загалом, присвоєння вживаються в межах оголошень змінних (тобто вкупі з const
, let
чи var
) або ж як окремі інструкції.
// Оголошується змінна x, а потім ініціалізується результатом f().
// Результат виразу присвоєння x = f() – відкидається.
let x = f();
x = g(); // Повторно присвоює змінній x результат g().
А проте, подібно до інших виразів, вирази присвоєння виду x = f()
обчислюються до значення результату. Хоч зазвичай цей результат не використовується, він може бути використаний іншим виразом.
Утворення ланцюжків присвоєнь або присвоєнь, вкладених в інші вирази, може призвести до неочікуваної поведінки. Через це частина посібників зі стилю JavaScript коду не радить утворювати з присвоєнь ланцюжки або вкладеність. Попри це, іноді ланцюжки та вкладеність присвоєнь можуть траплятися, тому важливо розуміти, як вони працюють.
При додаванні присвоєння в ланцюжок або вкладеність, результат такого присвоєння сам може бути присвоєний іншій змінній. Він може бути виведений, вписаний в літерал масиву або виклик функції, і так далі.
let x;
const y = (x = f()); // Або рівносильне: const y = x = f();
console.log(y); // Виводить повернене значення присвоєння x = f().
console.log(x = f()); // Безпосередньо виводить повернене значення.
// Вираз присвоєння може бути вкладений в будь-якому місці,
// в якому вирази загалом дозволені,
// як то елементи літералів масивів або аргументи викликів функцій.
console.log([0, x = f(), 0]);
console.log(f(0, x = f(), 0));
Результат обчислення відповідає виразові справа від знака =
у колонці "Значення" таблиці вище. Це означає, що x = f()
обчислюється в те, чим є результат f()
, x += f()
обчислюється в суму x + f()
, x **= f()
обчислюється в піднесення до степеня x ** f()
, і так далі.
Що до логічних присвоєнь, x &&= f()
, x ||= f()
і x ??= f()
, то повернене значення – значення логічної операції без присвоєння, тобто x && f()
, x || f()
та x ?? f()
відповідно.
При утворенні ланцюжків з таких виразів без дужок, або інакшого групування операторів – наприклад, в літералах масивів, вирази присвоєння групуються справа наліво (вони мають праву асоціативність), але обчислюються зліва направо.
Зверніть увагу на те, що для всіх операторів присвоєння, крім самого =
, результівне значення – завжди засноване на значеннях операндів до операції.
Наприклад, оголошені такі функції – f
і g
, а також змінні x
та y
:
function f() {
console.log("F!");
return 2;
}
function g() {
console.log("G!");
return 3;
}
let x, y;
Погляньте на ці три приклади:
y = x = f();
y = [f(), x = g()];
x[f()] = g();
Приклад обчислення №1
Запис y = x = f()
– рівносильний щодо y = (x = f())
, адже оператор присвоєння =
має праву асоціативність.
Проте обчислюється він зліва направо:
- Починається обчислення виразу присвоєння
y = x = f()
.y
зліва цього присвоєння обчислюється в посилання на зміннуy
.- Починається обчислення виразу
x = f()
.x
зліва цього присвоєння обчислюється в посилання на зміннуx
.- Виклик функції
f()
виводить у консоль "F!", а тоді обчислюється в число2
. - Цей результат
2
зf()
– присвоюєтьсяx
.
- Вираз присвоєння
x = f()
завершив виконання; його результат став новим значеннямx
, а саме –2
. - Цей результат
2
й собі присвоюєтьсяy
.
- Вираз присвоєння
y = x = f()
завершив виконання; його результат став новим значеннямy
– котре виявилося2
.x
іy
присвоєно значення2
, а консоль вивела "F!".
Приклад обчислення №2
y = [ f(), x = g() ]
так само обчислюється зліва направо:
- Починається обчислення виразу присвоєння
y = [ f(), x = g() ]
.y
зліва цього присвоєння обчислюється в посилання на зміннуy
.- Починається обчислення вбудованого літерала масиву
[ f(), x = g() ]
.- Виклик функції
f()
виводить у консоль "F!", а тоді обчислюється в число2
. - Починається обчислення виразу присвоєння
x = g()
.x
зліва цього присвоєння обчислюється в посилання на зміннуx
.- Виклик функції
g()
виводить у консоль "G!" , а тоді обчислюється в число3
. - Цей результат
3
зg()
– присвоюєтьсяx
.
- Вираз присвоєння
x = g()
завершив виконання; його результат став новим значеннямx
, а саме –3
. Це результівне3
стає новим елементом вбудованого літерала масиву (після2
зf()
).
- Виклик функції
- Вбудований літерал масиву
[ f(), x = g() ]
завершив виконання; його результат – масив з двома значеннями:[ 2, 3 ]
. - Цей масив
[ 2, 3 ]
присвоюєтьсяy
.
- Вираз присвоєння
y = [ f(), x = g() ]
завершив виконання; його результат стає новим значеннямy
– котре виявилося[ 2, 3 ]
. Таким чином,x
присвоєно3
,y
присвоєно[ 2, 3 ]
, а консоль вивела "F!", а потім "G!".
Приклад обчислення №3
Вираз x[f()] = g()
так само обчислюється зліва направо. (Цей приклад виходить з припущення, що x
уже присвоєний якийсь об'єкт. Більше про об'єкти читайте в Роботі з об'єктами.)
- Починається обчислення виразу
x[f()] = g()
.- Починається обчислення звертання до властивості
x[f()]
зліва цього присвоєння.x
у цьому звертанні до властивості обчислюється в посилання на зміннуx
.- Потім виклик функції
f()
виводить "F!" у консоль і обчислюється в число2
.
- Звертання до властивості
x[f()]
у цьому присвоєнні завершує виконання; його результатом є посилання на змінну властивість:x[2]
. - Потім виклик функції
g()
виводить "G!" у консоль, а тоді обчислюється в число3
. - Тепер
3
присвоюєтьсяx[2]
. (Цей крок має успіх лише за умови того, щоx
присвоєно об'єкт.)
- Починається обчислення звертання до властивості
- Вираз присвоєння
x[f()] = g()
завершує виконання; його результатом є нове значенняx[2]
, а саме –3
. Теперx[2]
присвоєно3
, а консоль вивела "F!", і потім – "G!".
Слід уникати ланцюжків присвоєння
Ланцюжки присвоєнь і вкладання присвоєнь в інші вирази можуть призводити до дивної поведінки. Через це заведено уникати ланцюжків присвоєння в межах однієї інструкції.
Якщо конкретніше, то постановка ланцюжка змінних в інструкціях const
, let
і var
нерідко не працює. Лише зовнішня (найлівіша) змінна оголошується; решта змінних в межах такого ланцюжка присвоєння не оголошуються інструкцією const
, let
чи var
.
Наприклад:
const z = y = x = f();
Ця інструкція нібито оголошує змінні x
, y
і z
.
Проте фактично вона оголошує лише z
.
y
і x
є або недійсними посиланнями на відсутні змінні (в суворому режимі) або, що іще гірше, призводять до неявного створення глобальних змінних x
і y
, коли в недбалому режимі.
Оператори порівняння
Оператор порівняння порівнює свої операнди й повертає логічне значення, засноване на тому, чи є істинним порівняння.
Операнди можуть бути числовими, рядковими, логічними або об'єктними значеннями.
Рядки порівнюються на основі стандартного лексикографічного порядку, за допомогою значень Unicode.
У більшості випадків, якщо два операнди мають різні типи, то JavaScript намагається перетворити їх до відповідного типу задля порівняння.
Ця поведінка загалом призводить до числового порівняння операндів.
Єдиний виняток щодо перетворень типів при порівнянні включає оператори ===
і !==
, які виконують порівняння строгої рівності й строгої нерівності.
Ці оператори не намагаються перетворити операнди до сумісних типів перед їх порівнянням.
Наступна таблиця описує оператори порівняння відносно такого коду:
const var1 = 3;
const var2 = 4;
Оператор | Опис | Приклади, що повертають true |
---|---|---|
Рівності (== )
|
Повертає true , якщо операнди дорівнюють одне одному. |
3 == var1
3 == '3'
|
Нерівності (!= )
|
Повертає true , якщо операнди не дорівнюють одне одному. |
var1 != 4
|
Строгої рівності (=== )
|
Повертає true , якщо операнди дорівнюють одне одному й мають один і той же тип. Дивіться також Object.is і тотожність у JS.
|
3 === var1 |
Строгої нерівності (!== )
|
Повертає true , якщо операнди належать до одного типу, але не дорівнюють одне одному, або належать до різних типів.
|
var1 !== "3"
|
Більше (> )
|
Повертає true , якщо лівий операнд більший за правий.
|
var2 > var1
|
Більше або дорівнює
(>= )
|
Повертає true , якщо лівий операнд більший або дорівнює правому.
|
var2 >= var1
|
Менше
(< )
|
Повертає true , якщо лівий операнд менший за правий.
|
var1 < var2
|
Менше або дорівнює
(<= )
|
Повертає true , якщо лівий операнд менший або дорівнює правому.
|
var1 <= var2
|
[!NOTE]
=>
– це не оператор порівняння, а запис у стрілкових функціях.
Арифметичні оператори
Арифметичні оператори приймають як операнди числові значення (або літерали, або змінні), а повертають – єдине числове значення.
Стандартні арифметичні операції – це додавання (+
), віднімання (-
), множення (*
) та ділення (/
).
Ці оператори працюють так, як і в більшості решти мов програмування, коли застосовуються до дробових чисел (а саме, зверніть увагу на те, що ділення на нуль повертає нескінченність
). Наприклад:
1 / 2; // 0.5
1 / 2 === 1.0 / 2.0; // це істинно
На додачу до стандартних арифметичних операцій (+
, -
, *
, /
), JavaScript пропонує арифметичні операції, перелічені в наступній таблиці:
Оператор | Опис | Приклад |
---|---|---|
Остача від ділення (% )
|
Бінарний оператор. Повертає ціле число, котре є остачею від ділення двох операндів. | 12 % 5 returns 2. |
Інкремент (++ )
|
Унарний оператор. Додає до свого операнда одиницю. Бувши вжитим як префіксний оператор
(++x ), повертає значення свого операнда після додавання одиниці, а якщо як постфіксний (x++ ), то до додавання одиниці.
|
Якщо x має значення 3, то ++x присвоює x значення 4 і повертає 4, натомість x++ повертає 3, і лише після цього присвоює x значення 4.
|
Декремент (-- )
|
Унарний оператор. Віднімає від свого операнда одиницю. Повернене значення – аналогічне до поверненого значення оператора інкременту. |
Якщо x має значення 3, то --x присвоює x значення 2 і повертає 2, натомість x-- повертає 3, і лише після цього присвоює x значення 2.
|
Унарна протилежність (- )
|
Унарний оператор. Повертає протилежність до свого операнда. | Якщо x має значення 3, то -x повертає -3. |
Унарний плюс (+ )
|
Унарний оператор. Намагається перетворити свій операнд на число, якщо він ним не є. |
|
Піднесення до степеня (** )
|
Обчислює піднесення числа base до степеня exponent , тобто base^exponent
|
2 ** 3 повертає 8 .10 ** -1
повертає 0.1 .
|
Бітові оператори
Бітові оператори розглядають свої операнди як набори з 32 бітів (нулів та одиниць), а не як десяткові, шістнадцяткові або вісімкові числа. Наприклад, десяткове число дев'ять має двійкове представлення 1001. Бітові оператори виконують свої операції над таким двійковим представленням, але повертають звичайні числові значення JavaScript.
Наступна таблиця містить підсумок бітових операторів JavaScript.
Оператор | Застосування | Опис |
---|---|---|
Бітова кон'юнкція | a & b |
Повертає одиницю в кожній бітовій позиції, в якій відповідні біти обох операндів – одиниці. |
Бітова диз'юнкція | a | b |
Повертає нуль в кожній бітовій позиції, в якій відповідні біти обох операндів – нулі. |
Бітова виключна диз'юнкція | a ^ b |
Повертає нуль у кожній бітовій позиції, в якій відповідні біти – однакові (Повертає одиницю в кожній бітовій позиції, в якій відповідні біти – різні).] |
Бітове заперечення | ~ a |
Міняє біти свого операнда на протилежні. |
Зсув уліво | a << b |
Зсуває a у двійковому представленні на b бітів уліво, вставляючи нулі справа. |
Зсув управо зі збереженням знаку | a >> b |
Зсуває a у двійковому представленні на b бітів управо, відкидаючи висунуті біти. |
Зсув управо з додаванням нулів | a >>> b |
Зсуває a у двійковому представленні на b бітів управо, відкидаючи висунуті біти, і доставляючи зліва нулі. |
Бітові логічні оператори
Концептуально бітові логічні оператори працюють так:
-
Операнди перетворюються на тридцятидвобітні цілі числа, виражені послідовностями бітів (нулів та одиниць). Найважливіші біти чисел, котрі мають більш ніж 32 біти, відкидаються. Наприклад, наступне ціле число, котре має понад 32 біти, перетворюється на 32-бітове ціле:
До: 1110 0110 1111 1010 0000 0000 0000 0110 0000 0000 0001 Після: 1010 0000 0000 0000 0110 0000 0000 0001
-
Кожний біт першого операнда зіставляється з відповідним бітом другого операнда: перший біт до першого біта, другий біт до другого біта – і так далі.
-
Оператор застосовується до кожної пари бітів, а результат збирається з бітових результатів.
Наприклад, двійкове представлення дев'яти – 1001, а двійкове представлення п'ятнадцяти – 1111. Таким чином, коли до цих значень застосувати бітові оператори, то результати будуть такі:
Вираз | Результат | Двійковий опис |
---|---|---|
15 & 9 |
9 |
1111 & 1001 = 1001 |
15 | 9 |
15 |
1111 | 1001 = 1111 |
15 ^ 9 |
6 |
1111 ^ 1001 = 0110 |
~15 |
-16 |
~ 0000 0000 … 0000 1111 = 1111 1111 … 1111 0000 |
~9 |
-10 |
~ 0000 0000 … 0000 1001 = 1111 1111 … 1111 0110 |
Зверніть увагу на те, що всі 32 біти – змінюються на протилежні за допомогою оператора бітового заперечення, і що значення з найважливішим (крайнім зліва) бітом 1 представляють від'ємні числа (доповнювальне представлення). ~x
обчислюється в таке ж значення, що й -x - 1
.
Оператори бітового зсуву
Оператори бітового зсуву приймають два операнди: перший – число до зсуву, а другий – це число бітових позицій, на яке буде зсунутий перший операнд. Напрям операції зсуву визначається застосованим оператором.
Оператори зсуву перетворюють свої операнди на тридцятидвобітні цілі числа й повертають результат або типуNumber
, або BigInt
, а саме: якщо тип лівого операнда – BigInt
, то вони повертають BigInt
, інакше – Number
.
Оператори зсуву перелічені в наступній таблиці.
Оператори | Опис | Приклад |
---|---|---|
Зсув уліво ( << )
|
Цей оператор зсуває перший операнд на задану кількість бітів уліво. Надлишкові біти зліва – відкидаються. Справа – доставляються нулі. |
9<<2 видає 36, тому що 1001, зсунуте на 2 біти уліво, стає 100100, тобто 36.
|
Зсув управо зі збереженням знаку (>> )
|
Цей оператор зсуває перший операнд на задану кількість бітів управо. Надлишкові біти справа – відкидаються. Зліва доставляються копії крайнього лівого біта. |
9>>2 видає 2, тому що 1001, зсунуте на 2 бітів управо, стає 10, тобто 2. Аналогічно, -9>>2 видає -3, тому що знак зберігається.
|
Зсув управо з додаванням нулів (>>> )
|
Цей оператор зсуває перший операнд на задану кількість бітів управо. Надлишкові біти справа – відкидаються. Зліва – доставляються нульові біти. |
19>>>2 видає 4, адже 10011, зсунуте на 2 біти управо, стає 100, тобто 4. У випадку невід'ємних чисел зсув управо із додаванням нулів і зсув управо зі збереженням знаку видають однакові результати.
|
Логічні оператори
Логічні оператори здебільшого застосовуються на булевих (логічних) значеннях; коли це так, то вони повертають булеві значення.
Проте оператори &&
, ||
і ??
фактично повертають значення одного з переданих операндів, тож коли ці оператори застосовуються до небулевих значень, то ці оператори можуть повернути небулеві значення. Отже, їх більш доречно було б звати "операторами вибору значення".
Логічні оператори описані в наступній таблиці.
Оператор | Застосування | Опис |
---|---|---|
Логічна кон'юнкція (&& )
|
expr1 && expr2 |
Повертає expr1 , якщо це значення може бути перетворено на false , інакше – повертає expr2 . Таким чином, бувши застосованим до булевих значень, оператор && повертає true , якщо обидва операнди – true, інакше – повертає false .
|
Логічна диз'юнкція (|| )
|
expr1 || expr2 |
Повертає expr1 , якщо це значення може бути перетворено на true , інакше повертає expr2 . Таким чином, бувши застосованим до булевих значень, оператор || повертає true , якщо будь-який з операндів – true, якщо ж обидва вони – false, то повертає false .
|
Оператор null-злиття (?? )
|
expr1 ?? expr2 |
Повертає expr1 , якщо цей вираз не є ані null , ані
undefined ; інакше – повертає expr2 .
|
Логічне заперечення (! )
|
!expr |
Повертає false , якщо єдиний операнд оператора може бути перетворений на true , інакше – повертає true .
|
Прикладами виразів, котрі можуть бути перетворені на false
, є ті вирази, котрі обчислюються в null
, 0
, 0n
, NaN
, порожній рядок (""
) та undefined.
Наступний код демонструє приклади застосування оператора &&
(логічної кон'юнкції).
const a1 = true && true; // t && t повертає true
const a2 = true && false; // t && f повертає false
const a3 = false && true; // f && t повертає false
const a4 = false && 3 === 4; // f && f повертає false
const a5 = "Кіт" && "Пес"; // t && t повертає Пес
const a6 = false && "Кіт"; // f && t повертає false
const a7 = "Кіт" && false; // t && f повертає false
Наступний код демонструє приклади застосування оператора ||
(логічної диз'юнкції).
const o1 = true || true; // t || t повертає true
const o2 = false || true; // f || t повертає true
const o3 = true || false; // t || f повертає true
const o4 = false || 3 === 4; // f || f повертає false
const o5 = "Кіт" || "Пес"; // t || t повертає Кіт
const o6 = false || "Кіт"; // f || t повертає Кіт
const o7 = "Кіт" || false; // t || f повертає Кіт
Наступний код демонструє приклади оператора ??
(null-злиття).
const n1 = null ?? 1; // 1
const n2 = undefined ?? 2; // 2
const n3 = false ?? 3; // false
const n4 = 0 ?? 4; // 0
Зверніть увагу на те, що ??
працює подібно до ||
, але повертає другий вираз лише тоді, коли перший є "нульовим", тобто null
або undefined
. ??
кращий за ||
для задання усталених значень там, де може бути null
або undefined
, особливо коли значення штибу ''
і 0
є дійсними значеннями, що не повинні замінюватися на усталені.
Наступний код демонструє приклади застосування оператора !
(логічного заперечення).
const n1 = !true; // !t повертає false
const n2 = !false; // !f повертає true
const n3 = !"Кіт"; // !t повертає false
Закорочення обчислення
Оскільки логічні вирази обчислюються зліва направо, то вони перевіряються на змогу "закоротити" обчислення, згідно з наступними правилами:
хибне && щось
– із закороченням обчислюється в хибне значення.істинне || щось
– із закороченням обчислюється в істинне значення.неНульове ?? щось
– із закороченням обчислюється в ненульове значення.
Правила логіки гарантують, що такі обчислення завжди є коректними. Зверніть увагу на те, що частина щось виразів вище – не обчислюється, тож жодні побічні ефекти цих частин – не подіють.
Оператори BigInt
Більшість операторів, котрі можуть бути застосовані до двох чисел, може бути застосована також до значень BigInt
.
// Додавання BigInt
const a = 1n + 2n; // 3n
// Ділення BigInts округлюється в бік нуля
const b = 1n / 2n; // 0n
// Бітові операції над BigInts не обрізають число з жодного боку
const c = 40000000000000000n >> 2n; // 10000000000000000n
Винятком є беззнаковий зсув управо (>>>
), котрий не визначений для значень BigInt. Це пов'язано з тим, що BigInt не має фіксованої ширини, тож технічно цей тип не має "крайнього зліва біта".
const d = 8n >>> 2n; // TypeError: BigInts have no unsigned right shift, use >> instead
Значення BigInt і числа не є взаємозамінними – їх не можна змішувати при обчисленнях.
const a = 1n + 2; // TypeError: Cannot mix BigInt and other types
Це пов'язано з тим, що BigInt не є ані підмножиною, ані надмножиною чисел. Значення BigInt мають вищу точність, ніж звичайні числа, при представленні великих цілих, але не можуть представляти дроби, тож неявне перетворення в обидва боки може призвести до втрати точності. Слід використовувати явне перетворення для позначення того, чи повинна операція відбуватися над звичайними числами, чи над BigInt.
const a = Number(1n) + 2; // 3
const b = 1n + BigInt(2); // 3n
Можна порівнювати BigInt з числами.
const a = 1n > 2; // false
const b = 3 > 2n; // true
Рядкові оператори
На додачу до операторів порівняння, котрі можна застосовувати до рядкових значень, оператор зчеплення (+) зчіплює два рядкові значення докупи, повертаючи новий рядок, котрий є сполученням двох рядків – операндів.
Наприклад,
console.log("my " + "string"); // консоль виводить рядок "my string".
Оператор присвоєння зі скороченням +=
також може використовуватись для зчеплення рядків.
Наприклад,
let myString = "alpha";
myString += "bet"; // обчислюється в "alphabet" й присвоює це значення myString.
Умовний (тернарний) оператор
Умовний оператор – це єдиний оператор JavaScript, котрий приймає три операнди. Він може приймати одне з двох значень, залежно від умови. Запис – наступний:
condition ? val1 : val2
Якщо condition
істинна, то оператор має значення val1
.
Інакше – він має значення val2
. Умовний оператор можна використовувати всюди, де можна було б використати стандартний оператор.
Наприклад,
const status = age >= 18 ? "adult" : "minor";
Ця інструкція присвоює значення "adult" змінній status
, якщо age
має значення вісімнадцяти або більше. Інакше, змінній status
присвоюється значення "minor".
Оператор коми
Оператор коми (,
) обчислює обидва свої операнди й повертає значення останнього з них.
Цей оператор перш за все використовується в циклі for
, аби мати змогу оновлювати декілька змінних при кожній ітерації циклу.
Вживання оператора коми вважається недоброю практикою, коли це не є необхідністю.
Нерідко замість цього можна й варто використати дві окремі інструкції.
Наприклад, якщо a
– це двовимірний масив з 10 елементами в кожному підмасиві, то наступний код використовує оператор коми, аби оновити дві змінні за раз.
Цей код виводить значення діагональних елементів масиву:
const x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const a = [x, x, x, x, x];
for (let i = 0, j = 9; i <= j; i++, j--) {
// ^
console.log(`a[${i}][${j}]= ${a[i][j]}`);
}
Унарні оператори
Унарна операція – це операція лише з одним операндом.
delete
Оператор delete
видаляє властивість об'єкта.
Запис – наступний:
delete object.property;
delete object[propertyKey];
delete objectName[index];
де object
– це назва об'єкта, property
– його наявна властивість, а propertyKey
– рядок або символ, що вказує на наявну властивість.
Коли оператор delete
має успіх, то прибирає з об'єкта властивість.
Спроба звернутись до неї після цього видасть undefined
.
Оператор delete
повертає true
, якщо операція можлива, і false
, якщо вона неможлива.
delete Math.PI; // повертає false (не можна видаляти неналаштовні властивості)
const myObj = { h: 4 };
delete myObj.h; // повертає true (визначені користувачем властивості видаляти можна)
Видалення елементів масиву
Оскільки масиви є лишень об'єктами, технічно можливо застосувати до них оператор delete
.
Проте це вважається недоброю практикою, так робити не варто.
Коли видаляється властивість масиву, то це не впливає на його довжину, а решта елементів не отримує нових індексів.
Для прибирання елемента з масиву краще просто записати замість нього значення undefined
.
Задля фактичного змінювання масиву слід використовувати різні методи масиву, як то splice
.
typeof
Оператор typeof
повертає рядок, котрий вказує на тип необчисленого операнда.
operand
– це рядок, змінна, ключове слово або об'єкт, тип якого буде повернений.
Дужки – необов'язкові.
Припустімо, що оголошені наступні змінні:
const myFun = new Function("5 + 2");
const shape = "round";
const size = 1;
const foo = ["Apple", "Mango", "Orange"];
const today = new Date();
Оператор typeof
поверне для них наступні результати:
typeof myFun; // повертає "function"
typeof shape; // повертає "string"
typeof size; // повертає "number"
typeof foo; // повертає "object"
typeof today; // повертає "object"
typeof doesntExist; // повертає "undefined"
Для ключових слів true
і null
оператор typeof
повертає наступні результати:
typeof true; // повертає "boolean"
typeof null; // повертає "object"
Для числа або рядка:
typeof 62; // повертає "number"
typeof "Hello world"; // повертає "string"
Для значень властивостей оператор typeof
повертає тип значення, котре така властивість містить:
typeof document.lastModified; // повертає "string"
typeof window.length; // повертає "number"
typeof Math.LN2; // повертає "number"
Для методів і функцій оператор typeof
повертає таке:
typeof blur; // повертає "function"
typeof eval; // повертає "function"
typeof parseInt; // повертає "function"
typeof shape.split; // повертає "function"
Для наперед визначених об'єктів:
typeof Date; // повертає "function"
typeof Function; // повертає "function"
typeof Math; // повертає "object"
typeof Option; // повертає "function"
typeof String; // повертає "function"
void
Оператор void
позначає вираз як такий, що повинен бути обчислений без повернення значення. expression
– це вираз JavaScript для обчислення.
Дужки навколо виразу – необов'язкові, але краще їх використовувати, аби уникати проблем з пріоритетом.
Оператори відношення
Оператори відношення порівнюють свої операнди й повертають булеве значення на основі істинності порівняння.
in
Оператор in
повертає true
, якщо дана властивість існує в даному об'єкті.
Запис – наступний:
propNameOrNumber in objectName
де propNameOrNumber
– це рядковий, числовий або символьний вираз, що представляє назву властивості або індекс масиву, а objectName
– це назва об'єкта.
Наступні приклади демонструють кілька варіантів використання оператора in
.
// Масиви
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // повертає true
3 in trees; // повертає true
6 in trees; // повертає false
"bay" in trees; // повертає false
// (необхідно задати число-індекс, а не значення за цим індексом)
"length" in trees; // повертає true (length – це властивість масива)
// вбудовані об'єкти
"PI" in Math; // повертає true
const myString = new String("coral");
"length" in myString; // повертає true
// Власні об'єкти
const myCar = { make: "Honda", model: "Accord", year: 1998 };
"make" in myCar; // повертає true
"model" in myCar; // повертає true
instanceof
Оператор instanceof
повертає true
, якщо даний об'єкт належить до даного об'єктного типу. Запис – наступний:
object instanceof objectType
де object
– це об'єкт для перевірки щодо objectType
, а objectType
– конструктор, що представляє тип, наприклад, Date
або Array
.
Використовувати instanceof
слід тоді, коли необхідно перевірити тип об'єкта під час виконання.
Наприклад, при перехопленні винятків, може бути потреба обробляти по-різному викинуті винятки, залежно від їх типу.
Наприклад, наступний код використовує instanceof
для з'ясування того, чи є theDay
об'єктом Date
. Оскільки theDay
є об'єктом Date
, то інструкції всередині інструкції if
– виконуються.
const theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
// інструкції до виконання
}
Базові вирази
Усі оператори, зрештою, працюють з одним чи багатьма базовими виразами. Серед цих базових виразів – ідентифікатори й літерали, однак є й кілька інших різновидів. Ці різновиди стисло вводяться нижче, а їхня семантика детально описана у відповідних розділах довідки.
this
Ключове слово this
слід використовувати для звертання до поточного об'єкта.
Загалом, this
вказує на об'єкт, з якого викликається метод.
this
слід вживати із записом крапки або квадратних дужок:
this["propertyName"];
this.propertyName;
Припустімо, що функція під назвою validate
валідує властивість value
об'єкта, отримавши об'єкт та межі допустимого діапазону для value
:
function validate(obj, lowVal, highVal) {
if (obj.value < lowVal || obj.value > highVal) {
console.log("Недійсне значення!");
}
}
validate
можна викликати в кожному обробнику подій onChange
елемента форми, використовуючи this
для передачі цього елемента, як це показано в наступному прикладі:
<p>Введіть число між 18 і 99:</p>
<input type="text" name="age" size="3" onChange="validate(this, 18, 99);" />
Оператор групування
Оператор групування ( )
контролює пріоритет обчислення у виразах. Наприклад, можна відкинути першість множення й ділення перед додаванням і відніманням – і обчислити спершу додавання.
const a = 1;
const b = 2;
const c = 3;
// усталений пріоритет
a + b * c // 7
// усталено обчислюється так
a + (b * c) // 7
// тепер нав'язуємо пріоритет
// додавання перед множенням
(a + b) * c // 9
// що рівносильно
a * c + b * c // 9
Аксесор властивості
Синтаксис аксесора властивості отримує значення властивостей на об'єктах, користуючись або крапковим, або дужковим записом.
object.property;
object["property"];
Посібник роботи з об'єктами вміщає більше подробиць про властивості об'єктів.
Необов'язковий ланцюжок
Синтаксис необов'язкового ланцюжка (?.
) виконує операцію в ланцюжку на об'єкті, якщо він визначений і не є null
, а інакше – закорочує операцію та повертає undefined
.
Це дає змогу працювати зі значенням, що може бути null
або undefined
, не спричиняючи TypeError
.
maybeObject?.property;
maybeObject?.[property];
maybeFunction?.();
new
Оператор new
можна використовувати для створення примірника власного об'єктного типу або одного із вбудованих об'єктних типів. Робиться це так:
const objectName = new ObjectType(param1, param2, /* …, */ paramN);
super
Ключове слово super
використовується для виклику функцій на батьківському об'єкті.
Корисно, наприклад, при використанні класів – викликати батьківський конструктор.
super(args); // викликається батьківський конструктор.
super.functionOnParent(args);