Как делать11 октября 2026 г.6 мин чтения
JavaScript с нуляШаг 3 из 9

Условия и циклы

Коротко

Условия позволяют программе выбирать, что делать: if выполняет блок кода, только если выражение истинно, else if и else задают другие варианты, а switch удобен для выбора по одному значению. Циклы повторяют действие: for — заданное число раз, for...of — для каждого элемента массива, while — пока условие истинно. У каждого цикла должно быть условие выхода, иначе программа зависнет.

Пошаговый урокУровень: НачальныйУсловия и циклыПрактика: ~45–90 мин

После материала вы сможете

  • назвать все ложные (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 г.

Содержание
  1. 01Логические выражения и truthy/falsy
  2. 02if, else if, else и тернарный оператор
  3. 03switch и когда лучше объект-словарь
  4. 04Циклы for, for...of и while
  5. 05break, continue и условие выхода
  6. 06Практика: FizzBuzz и подсчёт итогов заказа
  7. 07Типичные ошибки: off-by-one и бесконечный цикл
  8. 08Упражнения и что дальше

Логические выражения и truthy/falsy

Любое решение в программе начинается с вопроса, на который можно ответить «да» или «нет»: сумма больше 15 000? товар закончился? поле заполнено? Такой вопрос в коде — логическое выражение, а его ответ — значение true или false. Их дают операторы сравнения (===, !==, >, <, >=, <=) и логические операторы: && — «и», || — «или», ! — «не».

Условию не обязательно получать именно true или false. Если подставить в if число или строку, JavaScript сам приведёт значение к логическому. По MDN, ложными (falsy) считаются ровно восемь значений: false, 0, -0, 0n, пустая строка "", null, undefined и NaN (плюс экзотический document.all в браузере). Все остальные значения — истинные (truthy), в том числе строка "0", строка из пробела и пустой массив.

truthy.js
// Ложные (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);  // → false
Boolean(значение) показывает, как значение поведёт себя в условии.

if, else if, else и тернарный оператор

if проверяет условие в круглых скобках и выполняет блок в фигурных, если оно истинно. else if добавляет следующую проверку, else срабатывает, если не подошёл ни один вариант. Проверки идут сверху вниз, и выполняется только первая подходящая ветка — поэтому большие пороги ставят выше маленьких. MDN советует всегда писать фигурные скобки, даже если в ветке одна строка: так проще добавить код и не ошибиться.

if-else.js
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 ₸
Сумма 23 500 ₸ не дотягивает до 50 000, но проходит порог 20 000 — срабатывает вторая ветка.

Тернарный оператор условие ? значение1 : значение2 — короткая форма для случая, когда нужно выбрать одно из двух значений. Цепочки тернарных операторов технически возможны, но читаются хуже, чем if…else if: для трёх и более вариантов лучше обычные ветки.

Теперь вернёмся к калькулятору чаевых из первого урока. Без аргументов он печатал NaN — исправим это условием. Заодно процент станет необязательным: оператор ?? подставит 10, если аргумент не передан. process.exitCode = 1 сообщает системе, что программа завершилась с ошибкой: это пригодится, когда скрипты запускают другие программы.

tip.js
// 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.js: process.argv и process.exitCode есть только там.
terminal
$ 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.

or-vs-nullish.js
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);                // → 0
Разница видна только на ложных значениях: 0 и пустой строке.

switch и когда лучше объект-словарь

Когда нужно выбрать действие по одному значению — статусу заказа, дню недели, типу оплаты, — длинная цепочка else if читается тяжело. switch вычисляет выражение и ищет первый case с тем же значением; по MDN, сравнение строгое, как ===. Блок default срабатывает, если совпадений нет.

switch.js
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 и вариант с объектом дают одинаковый результат.

switch

  • Подходит, когда в каждой ветке разные действия
  • Можно объединить несколько case в одну ветку
  • Легко забыть break

Объект-словарь

  • Подходит, когда каждому значению соответствуют данные: текст, цена, иконка
  • Короче и легко расширяется — новая строка в объекте
  • Значение по умолчанию задаётся через ??

Одна тонкость словаря: у обычного объекта есть унаследованные свойства, например toString, поэтому labels["toString"] вернёт не undefined, а функцию. Если ключ приходит от пользователя, проверяйте его через Object.hasOwn(labels, key) или храните словарь в Map — подробнее в уроке о структурах данных.

Циклы for, for...of и while

Цикл повторяет блок кода, пока выполняется условие. Каждое повторение называют итерацией. В JavaScript несколько видов циклов, но для начала хватит трёх.

loops.js
// 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}`);      // → Недель: 4
Строки // → после цикла — то, что он выведет по порядку.

for (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 сразу выходит из цикла — когда нужное уже найдено и перебирать дальше незачем.

break-continue.js
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}`); // → Первым закончился: Латте
continue пропускает напитки с нулевым остатком, break останавливает поиск на первом найденном.

Главное правило любого цикла — условие выхода. MDN прямо предупреждает: убедитесь, что условие цикла в какой-то момент станет ложным. Если забыть изменить переменную, от которой зависит условие, цикл не закончится никогда:

infinite.js
let i = 0;
while (i < 3) {
  console.log(i);
  // забыли i++ — условие никогда не станет ложным
}
Не запускайте: программа будет бесконечно печатать 0. Если всё же запустили — остановите её в терминале клавишами Ctrl+C.

Практика: FizzBuzz и подсчёт итогов заказа

FizzBuzz — классическая задача на условия внутри цикла: для чисел от 1 до 15 вывести «Fizz» вместо кратных трём, «Buzz» вместо кратных пяти и «FizzBuzz» вместо кратных обоим. Кратность проверяют оператором остатка от деления %: n % 3 === 0 значит «делится на 3 без остатка».

fizzbuzz.js
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
Проверка на 15 стоит первой: иначе для 15 сработала бы ветка Fizz, и до FizzBuzz дело бы не дошло.

Теперь задача ближе к работе: посчитать итог заказа в кофейне. Позиции с нулевым количеством пропускаем, от 10 000 ₸ даём скидку 5%, от 15 000 ₸ — бесплатную доставку.

order-total.js
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] не существует.

off-by-one.js
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);
Файл с ошибкой.
terminal
$ 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.0
Путь к файлу сокращён, строки стека опущены.

Cannot 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.

Расскажите о задаче — мы изучим запрос и свяжемся с вами

Есть задача?
Обсудим и оценим — бесплатно.

Расскажите о продукте или проблеме. Предложим архитектуру, срок и стоимость первой версии после изучения задачи.

  • Оценка объёма и рисков до старта
  • NDA — по вашему или нашему шаблону
  • Старт работ в течение недели

Не присылайте через форму пароли, ключи доступа и исходный код — безопасную передачу доступа обсудим отдельно.

Нажимая кнопку, вы соглашаетесь с обработкой персональных данных.