Условия позволяют программе выбирать, что делать: if выполняет блок кода, только если выражение истинно, else if и else задают другие варианты, а switch удобен для выбора по одному значению. Циклы повторяют действие: for — заданное число раз, for...of — для каждого элемента массива, while — пока условие истинно. У каждого цикла должно быть условие выхода, иначе программа зависнет.
После материала вы сможете
- назвать все ложные (falsy) значения JavaScript и предсказать, как значение поведёт себя в условии
- написать ветвление через if / else if / else, тернарный оператор и switch и выбрать между switch и объектом-словарём
- обойти массив циклами for, for...of и while и управлять ими через break и continue
- найти и исправить ошибку «на единицу» и бесконечный цикл
Что знать заранее
Проверено на
Node.js 24 LTS (24.18.0)Microsoft Edge 155
Сверено с документацией: 11 октября 2026 г.
Содержание
Логические выражения и truthy/falsy
Любое решение в программе начинается с вопроса, на который можно ответить «да» или «нет»: сумма больше 15 000? товар закончился? поле заполнено? Такой вопрос в коде — логическое выражение, а его ответ — значение true или false. Их дают операторы сравнения (===, !==, >, <, >=, <=) и логические операторы: && — «и», || — «или», ! — «не».
Условию не обязательно получать именно true или false. Если подставить в if число или строку, JavaScript сам приведёт значение к логическому. По MDN, ложными (falsy) считаются ровно восемь значений: false, 0, -0, 0n, пустая строка "", null, undefined и NaN (плюс экзотический document.all в браузере). Все остальные значения — истинные (truthy), в том числе строка "0", строка из пробела и пустой массив.
// Ложные (falsy) значения
console.log(Boolean(0)); // → false
console.log(Boolean("")); // → false
console.log(Boolean(null)); // → false
console.log(Boolean(undefined)); // → false
console.log(Boolean(NaN)); // → false
// Всё остальное — истинное (truthy), даже «похожее на пустое»
console.log(Boolean("0")); // → true
console.log(Boolean(" ")); // → true
console.log(Boolean([])); // → true
// Сравнения и логические операторы дают true или false
const age = 20;
const hasTicket = true;
console.log(age >= 18 && hasTicket); // → true
console.log(age < 18 || !hasTicket); // → falseif, else if, else и тернарный оператор
if проверяет условие в круглых скобках и выполняет блок в фигурных, если оно истинно. else if добавляет следующую проверку, else срабатывает, если не подошёл ни один вариант. Проверки идут сверху вниз, и выполняется только первая подходящая ветка — поэтому большие пороги ставят выше маленьких. MDN советует всегда писать фигурные скобки, даже если в ветке одна строка: так проще добавить код и не ошибиться.
const total = 23500; // сумма заказа, ₸
let discount;
if (total >= 50000) {
discount = 10;
} else if (total >= 20000) {
discount = 5;
} else {
discount = 0;
}
console.log(`Скидка: ${discount}%`); // → Скидка: 5%
// Тернарный оператор: условие ? если да : если нет
const delivery = total >= 15000 ? 0 : 1500;
console.log(`Доставка: ${delivery} ₸`); // → Доставка: 0 ₸Тернарный оператор условие ? значение1 : значение2 — короткая форма для случая, когда нужно выбрать одно из двух значений. Цепочки тернарных операторов технически возможны, но читаются хуже, чем if…else if: для трёх и более вариантов лучше обычные ветки.
Теперь вернёмся к калькулятору чаевых из первого урока. Без аргументов он печатал NaN — исправим это условием. Заодно процент станет необязательным: оператор ?? подставит 10, если аргумент не передан. process.exitCode = 1 сообщает системе, что программа завершилась с ошибкой: это пригодится, когда скрипты запускают другие программы.
// tip.js — калькулятор чаевых с проверкой ввода
// Запуск: node tip.js <сумма счёта> [процент, по умолчанию 10]
const bill = Number(process.argv[2]);
const percent = Number(process.argv[3] ?? 10);
if (Number.isNaN(bill) || bill <= 0) {
console.log("Укажите сумму счёта числом больше нуля, например: node tip.js 12500");
process.exitCode = 1;
} else {
const tip = Math.round(bill * percent / 100);
console.log(`Чаевые ${percent}%: ${tip} ₸`);
console.log(`Итого: ${bill + tip} ₸`);
}$ node tip.js 12500
Чаевые 10%: 1250 ₸
Итого: 13750 ₸
$ node tip.js 12500 0
Чаевые 0%: 0 ₸
Итого: 12500 ₸
$ node tip.js
Укажите сумму счёта числом больше нуля, например: node tip.js 12500Почему здесь ??, а не привычное ||? Оператор || возвращает правое значение, если левое ложно — а ложен и 0. Гость, который сознательно ввёл 0% чаевых, получил бы 10%. Оператор ?? подставляет значение по умолчанию только для null и undefined.
const input = "0"; // гость ввёл 0% чаевых
const withOr = Number(input) || 10; // 0 — falsy, и || подставит 10
const withNullish = Number(input ?? 10); // ?? срабатывает только на null и undefined
console.log(withOr); // → 10
console.log(withNullish); // → 0switch и когда лучше объект-словарь
Когда нужно выбрать действие по одному значению — статусу заказа, дню недели, типу оплаты, — длинная цепочка else if читается тяжело. switch вычисляет выражение и ищет первый case с тем же значением; по MDN, сравнение строгое, как ===. Блок default срабатывает, если совпадений нет.
const status = "paid";
switch (status) {
case "new":
console.log("Заказ принят");
break;
case "paid":
console.log("Оплачен, собираем");
break;
case "shipped":
console.log("Передан курьеру");
break;
default:
console.log("Неизвестный статус");
}
// → Оплачен, собираем
// То же самое через объект-словарь
const labels = {
new: "Заказ принят",
paid: "Оплачен, собираем",
shipped: "Передан курьеру",
};
console.log(labels[status] ?? "Неизвестный статус"); // → Оплачен, собираем
console.log(labels.lost ?? "Неизвестный статус"); // → Неизвестный статусswitch
- Подходит, когда в каждой ветке разные действия
- Можно объединить несколько case в одну ветку
- Легко забыть break
Объект-словарь
- Подходит, когда каждому значению соответствуют данные: текст, цена, иконка
- Короче и легко расширяется — новая строка в объекте
- Значение по умолчанию задаётся через ??
Одна тонкость словаря: у обычного объекта есть унаследованные свойства, например toString, поэтому labels["toString"] вернёт не undefined, а функцию. Если ключ приходит от пользователя, проверяйте его через Object.hasOwn(labels, key) или храните словарь в Map — подробнее в уроке о структурах данных.
Циклы for, for...of и while
Цикл повторяет блок кода, пока выполняется условие. Каждое повторение называют итерацией. В JavaScript несколько видов циклов, но для начала хватит трёх.
// for — когда известно, сколько раз повторить
for (let i = 1; i <= 3; i++) {
console.log(`Попытка ${i}`);
}
// → Попытка 1
// → Попытка 2
// → Попытка 3
// for...of — пройти по всем элементам массива
const items = [
{ name: "Капучино", price: 1600, qty: 2 },
{ name: "Круассан", price: 1200, qty: 1 },
{ name: "Вода", price: 500, qty: 3 },
];
let sum = 0;
for (const item of items) {
sum += item.price * item.qty;
}
console.log(`Итого: ${sum} ₸`); // → Итого: 5900 ₸
// while — когда число повторов заранее неизвестно
let saved = 0;
let weeks = 0;
while (saved < 10000) {
saved += 2500;
weeks++;
}
console.log(`Недель: ${weeks}`); // → Недель: 4for (let i = 1; i <= 3; i++)
- Как устроен
- Начальное значение; условие продолжения; шаг после каждой итерации
- Когда использовать
- Нужно повторить заданное число раз или нужен номер шага
for (const item of items)
- Как устроен
- На каждой итерации item — следующий элемент
- Когда использовать
- Обойти все элементы массива или символы строки
while (условие)
- Как устроен
- Проверяет условие перед каждой итерацией
- Когда использовать
- Число повторов заранее неизвестно: копим, ждём, ищем
do { … } while (условие)
- Как устроен
- Сначала выполняет блок, потом проверяет условие
- Когда использовать
- Тело должно выполниться хотя бы один раз
i++ — сокращение для i = i + 1. В for...of переменную объявляют через const: на каждой итерации создаётся новая. Есть ещё похожий цикл for...in, но он обходит имена свойств объекта, а не значения, — для массивов используйте for...of.
break, continue и условие выхода
Иногда нужно изменить ход цикла изнутри. continue прекращает текущую итерацию и переходит к следующей — удобно, чтобы пропустить неподходящие элементы. break сразу выходит из цикла — когда нужное уже найдено и перебирать дальше незачем.
const menu = [
{ name: "Капучино", left: 12 },
{ name: "Латте", left: 0 },
{ name: "Раф", left: 4 },
{ name: "Какао", left: 0 },
];
// continue — пропустить текущий шаг и перейти к следующему
for (const drink of menu) {
if (drink.left === 0) {
continue;
}
console.log(`В наличии: ${drink.name}`);
}
// → В наличии: Капучино
// → В наличии: Раф
// break — выйти из цикла сразу
let firstMissing = null;
for (const drink of menu) {
if (drink.left === 0) {
firstMissing = drink.name;
break;
}
}
console.log(`Первым закончился: ${firstMissing}`); // → Первым закончился: ЛаттеГлавное правило любого цикла — условие выхода. MDN прямо предупреждает: убедитесь, что условие цикла в какой-то момент станет ложным. Если забыть изменить переменную, от которой зависит условие, цикл не закончится никогда:
let i = 0;
while (i < 3) {
console.log(i);
// забыли i++ — условие никогда не станет ложным
}Практика: FizzBuzz и подсчёт итогов заказа
FizzBuzz — классическая задача на условия внутри цикла: для чисел от 1 до 15 вывести «Fizz» вместо кратных трём, «Buzz» вместо кратных пяти и «FizzBuzz» вместо кратных обоим. Кратность проверяют оператором остатка от деления %: n % 3 === 0 значит «делится на 3 без остатка».
for (let n = 1; n <= 15; n++) {
if (n % 15 === 0) {
console.log("FizzBuzz");
} else if (n % 3 === 0) {
console.log("Fizz");
} else if (n % 5 === 0) {
console.log("Buzz");
} else {
console.log(n);
}
}
// → 1
// → 2
// → Fizz
// → 4
// → Buzz
// → Fizz
// → 7
// → 8
// → Fizz
// → Buzz
// → 11
// → Fizz
// → 13
// → 14
// → FizzBuzzТеперь задача ближе к работе: посчитать итог заказа в кофейне. Позиции с нулевым количеством пропускаем, от 10 000 ₸ даём скидку 5%, от 15 000 ₸ — бесплатную доставку.
const cart = [
{ name: "Капучино", price: 1600, qty: 2 },
{ name: "Чизкейк", price: 2400, qty: 0 }, // убрали из корзины
{ name: "Зерно 250 г", price: 6900, qty: 1 },
{ name: "Круассан", price: 1200, qty: 3 },
];
let subtotal = 0;
let positions = 0;
for (const item of cart) {
if (item.qty <= 0) {
continue;
}
subtotal += item.price * item.qty;
positions++;
}
const discount = subtotal >= 10000 ? Math.round(subtotal * 0.05) : 0;
const delivery = subtotal >= 15000 ? 0 : 1500;
const total = subtotal - discount + delivery;
console.log(`Позиций: ${positions}`); // → Позиций: 3
console.log(`Сумма: ${subtotal} ₸`); // → Сумма: 13700 ₸
console.log(`Скидка: ${discount} ₸`); // → Скидка: 685 ₸
console.log(`Доставка: ${delivery} ₸`); // → Доставка: 1500 ₸
console.log(`К оплате: ${total} ₸`); // → К оплате: 14515 ₸Обратите внимание: порог бесплатной доставки здесь считается от суммы до скидки. Это бизнес-правило, а не правило языка — уточняйте такие вещи у заказчика до того, как писать код.
Типичные ошибки: off-by-one и бесконечный цикл
Ошибка «на единицу» (off-by-one) — самая частая в циклах. Элементы массива нумеруются с нуля, поэтому у массива из двух элементов индексы 0 и 1, а items.length равен 2. Условие i <= items.length делает лишнюю итерацию: items[2] не существует.
const items = [
{ name: "Капучино", price: 1600 },
{ name: "Круассан", price: 1200 },
];
let sum = 0;
for (let i = 0; i <= items.length; i++) { // ошибка: должно быть i < items.length
sum += items[i].price;
}
console.log(sum);$ node off-by-one.js
…\first-js\off-by-one.js:8
sum += items[i].price;
^
TypeError: Cannot read properties of undefined (reading 'price')
at …
Node.js v24.18.0Cannot read properties of undefined
- Причина
- Цикл вышел за конец массива
- Исправление
- i < items.length или for...of, где индекс не нужен
Программа зависла, вывод бесконечный
- Причина
- Условие цикла никогда не становится ложным
- Исправление
- Ctrl+C; проверить, что переменная условия меняется
Срабатывает не та ветка if
- Причина
- Пороги проверяются в неправильном порядке
- Исправление
- Ставить более строгие условия выше
Условие всегда истинно
- Причина
- В условии присваивание = вместо сравнения ===
- Исправление
- Сравнивать через ===; MDN не рекомендует присваивание в условии
0 или пустая строка заменились значением по умолчанию
- Причина
- || срабатывает на любое ложное значение
- Исправление
- Использовать ??
Упражнения и что дальше
Код урока уже повторяется: проверка суммы, расчёт чаевых, форматирование времени. Следующий шаг траектории — функции: как дать такому коду имя, передавать ему параметры и получать результат. Скорость алгоритмов — почему один цикл работает мгновенно, а вложенный тормозит на больших данных — разобрана в материале об алгоритмах и сложности.
Как проверено4 · пройденосверено 11 октября 2026 г.
- запущено, работает
truthy, if-else, or-vs-nullish, switch, loops, break-continue, fizzbuzz, order-total, slots (RU и EN)
node 24.18.0: stdout построчно сверен с комментариями // → (check-arrows), как .js и как .mjs; те же файлы выполнены в Microsoft Edge 155 headless — вывод совпал
- запущено, работает
tip.js и tip-range.js (Node.js, process.argv)
node 24.18.0 с аргументами 12500 / 12500 0 / без аргументов / abc / 12500 15 / 12500 50 / 12500 -5; shellsession в статье — реальный вывод
- запущено, работает
off-by-one.js
node 24.18.0 и Edge 155: TypeError: Cannot read properties of undefined (reading 'price'); в статье путь сокращён, стек опущен
- запущено, работает
infinite.js
запущен с таймаутом 1,5 с: процесс не завершился сам, печатал только 0, остановлен по таймауту (SIGTERM) — подтверждено, что цикл бесконечный; в статье помечен «не запускайте»
«Запущено» — код выполнен во временном проекте или контейнере, вывод в статье взят из прогона. «Частично» — проверено с заглушками вместо внешних сервисов. «Не запускалось» — нужны устройство, аккаунт или ключ; шаги сверены с официальной документацией. Файлы проверочного проекта и логи запусков сохранены в архиве InterLAB.