Пошук уроків, статей та іншого контенту
Розгалуження логіки в JavaScript: синтаксис if/else, guard clauses, switch і коли обирати кожен варіант.
Без умовних конструкцій програма могла б виконувати лише один заданий наперед сценарій. Розгалуження — це те, що дозволяє коду приймати рішення: показати знижку чи ні, дозволити вхід чи відмовити, обрати один із кількох варіантів відповіді. Практично кожна функція, яка робить щось корисніше за пряме обчислення, містить хоча б одну умову.
Вираз у дужках після if перевіряється на truthy/falsy (див. попередній урок) — це не обов'язково має бути true чи false буквально, будь-яке значення приводиться до булевого. Кожен блок { } виконується лише тоді, коли спрацьовує саме його умова — і жоден інший:
function getDiscount(age) {
if (age < 18) {
return 0;
} else if (age >= 65) {
return 0.2;
} else {
return 0.05;
}
}
getDiscount(16); // 0
getDiscount(70); // 0.2
getDiscount(30); // 0.05Важливо розуміти порядок обчислення: JavaScript перевіряє умови послідовно й зупиняється на першій, що спрацювала. Якщо переставити умови місцями чи зробити їх такими, що перекриваються, результат може бути неочікуваним:
function getGrade(score) {
if (score >= 50) return "склав";
if (score >= 90) return "відмінно"; // НІКОЛИ не виконається — попередня умова вже перехопила всі 50+
return "не склав";
}Якщо кілька умов можуть бути істинними одночасно, порядок перевірки критичний — розташовуйте вужчі, специфічніші умови перед ширшими.
Коли функція має кілька передумов, природно хочеться вкласти їх одна в одну — але це швидко стає важко читати. Ідіоматичний підхід у JavaScript — guard clauses (охоронні перевірки): вийти з функції одразу, щойно умова не виконується, замість нарощування вкладеності:
// Багато вкладеності — важко читати
function processOrder(order) {
if (order) {
if (order.items.length > 0) {
if (order.paid) {
return "Замовлення обробляється";
}
}
}
return "Неможливо обробити";
}
// Guard clauses — читається як список умов виходу
function processOrder(order) {
if (!order) return "Неможливо обробити";
if (order.items.length === 0) return "Неможливо обробити";
if (!order.paid) return "Неможливо обробити";
return "Замовлення обробляється";
}Коли логіка вкладається в один вираз, тернарний оператор читається природніше за if/else:
const status = age >= 18 ? "дорослий" : "неповнолітній";Не зловживайте вкладеними тернарними операторами — a ? b : c ? d : e важко читати. Для трьох і більше варіантів краще взяти switch, об'єкт-мапу або звичайний if/else.
switch не робить нічого, чого не можна зробити ланцюжком if / else if, але коли є багато дискретних варіантів однієї змінної, він читається зрозуміліше — усі можливі значення видно одразу, без повторення умови day === перед кожним варіантом:
function getDayName(day) {
switch (day) {
case 0:
return "Неділя";
case 1:
return "Понеділок";
case 6:
return "Субота";
default:
return "Робочий день";
}
}Забутий break — класична помилка: без нього виконання «провалюється» (fall-through) у наступний case, навіть якщо його умова не збігається. У прикладі вище кожен case використовує return, що автоматично зупиняє виконання — але якщо всередині case немає return, break обов'язковий.
Порівняння в switch відбувається через === (строге), тому case "1": не спрацює для числа 1 — так само, як у прикладі з типами з попереднього уроку.
Fall-through можна використати навмисно, якщо кілька значень мають привести до одного результату — тоді порожні case «падають» до спільного блоку коду:
function isWeekend(day) {
switch (day) {
case 0:
case 6:
return true; // і 0, і 6 потрапляють сюди
default:
return false;
}
}Коли кожен case просто повертає значення без додаткової логіки, об'єкт часто читається ще чистіше за switch — і не потребує break узагалі:
const DAY_NAMES = {
0: "Неділя",
1: "Понеділок",
6: "Субота",
};
function getDayName(day) {
return DAY_NAMES[day] ?? "Робочий день";
}Типова задача — перетворити код HTTP-відповіді на зрозуміле повідомлення для користувача:
function describeHttpStatus(status) {
if (status >= 200 && status < 300) return "Успішно";
if (status >= 300 && status < 400) return "Перенаправлення";
if (status === 401 || status === 403) return "Немає доступу";
if (status === 404) return "Не знайдено";
if (status >= 500) return "Помилка сервера";
return "Невідомий статус";
}
describeHttpStatus(404); // "Не знайдено"
describeHttpStatus(201); // "Успішно"Тут навмисно обрано if/else, а не switch: умови перевіряють діапазони (status >= 200 && status < 300), а switch добре працює лише для порівняння точних значень.
Забутий break призводить до виконання кількох case поспіль (якщо в них немає return).
Надмірна вкладеність if усередині if замість guard clauses або раннього return.
Спроба використати switch для перевірки діапазонів значень — він порівнює лише точні значення.
Плутанина порядку умов у ланцюжку else if, коли ширша умова «перехоплює» випадки, призначені для вужчої.
if/else — базовий інструмент для будь-якого розгалуження. Guard clauses прибирають зайву вкладеність у функціях з кількома передумовами. switch і об'єкт-мапа — зручніші альтернативи, коли порівнюєте одну змінну з набором точних значень. Тернарний оператор — лише для простих однорядкових виборів.
Спробуйте самостійно
Перепишіть функцію describeHttpStatus так, щоб вона додатково розрізняла 400 ("Некоректний запит") і 429 ("Забагато запитів"), не ламаючи наявну логіку для діапазонів.