</> Praktum

Главная / JavaScript / Циклы JavaScript

Урок · JavaScript

Циклы: повторяем действия без копирования

Редакция PraktumОбновлено 15 минут на урок с практикойУровень: новичок

Цикл в JavaScript выполняет блок кода несколько раз подряд — пока выполняется условие или пока не закончатся элементы. Для массивов удобнее всего for...of, когда нужен счётчик — классический for, а когда число повторов заранее неизвестно — while.

Коротко
  • for (const item of items) — перебор значений массива.
  • for (let i = 0; i < n; i++) — когда нужен номер шага.
  • while (условие) — пока условие истинно, число повторов неизвестно.
  • break завершает цикл, continue переходит к следующему шагу.
  • В цикле обязательно должно меняться условие, иначе вкладка зависнет.

Зачем этот урок сейчас

Вывести десять карточек, посчитать сумму корзины, проверить каждое поле формы — всё это повторение одного действия для разных данных. Копировать строку десять раз нельзя: данных может быть сто или ноль. Цикл делает одно действие столько раз, сколько нужно.

for...of: перебор массива

const topics = ['HTML', 'CSS', 'JavaScript'];

for (const topic of topics) {
  console.log(topic);
}
// HTML, CSS, JavaScript

На каждом шаге переменная topic получает очередной элемент. Это самый понятный способ перебрать массив, строку, Set или Map. Если нужен и индекс, используй entries(): for (const [i, topic] of topics.entries()).

Классический for

for (let i = 0; i < 5; i++) {
  console.log(`Шаг ${i + 1}`);
}

// обратный порядок
for (let i = items.length - 1; i >= 0; i--) {
  console.log(items[i]);
}

В скобках три части через точку с запятой: начальное значение счётчика, условие продолжения и изменение после каждого шага. Цикл идёт, пока условие истинно. Классический for нужен, когда важен номер шага, нестандартный шаг (i += 2) или перебор с конца.

Граница массива — i < items.length, а не <=: последний индекс на единицу меньше длины, и лишний шаг прочитает undefined.

while и do...while

let attempts = 0;
while (attempts < 3) {
  console.log(`Попытка ${attempts + 1}`);
  attempts++;
}

let answer;
do {
  answer = prompt('Введите «да»');
} while (answer !== 'да' && answer !== null);

while проверяет условие перед каждым шагом и может не выполниться ни разу. do...while сначала выполняет тело, потом проверяет — минимум один раз. Оба подходят, когда число повторов заранее неизвестно: читать данные, пока они есть, повторять запрос, пока не получится.

break и continue

const prices = [400, 0, 250, 9999, 700];
let total = 0;

for (const price of prices) {
  if (price === 0) continue;   // пропустить бесплатное
  if (price > 5000) break;      // ошибка в данных — остановиться
  total += price;
}
console.log(total); // 650

continue пропускает остаток текущего шага и переходит к следующему. break завершает весь цикл. Используй их, когда без них условие получается громоздким, но не заменяй ими обычный if в коротком теле.

for...in и перебор объекта

const prices = { html: 0, css: 1500, js: 2500 };

for (const key in prices) {
  console.log(key, prices[key]);
}

// современная запись — через массив пар
for (const [course, price] of Object.entries(prices)) {
  console.log(course, price);
}

for...in перебирает ключи объекта. Для массивов он не подходит: даёт индексы строками и может захватить лишние свойства. Для массива — for...of, для объекта — for...of по Object.entries.

Какой цикл выбрать

Выбор цикла
ЗадачаЦикл
Перебрать массив по значениямfor...of
Нужен номер шага или шаг не 1for
Число повторов неизвестноwhile
Выполнить хотя бы один разdo...while
Перебрать ключи объектаfor...of + Object.entries
Новый массив из старогоне цикл, а map / filter

Как не сделать бесконечный цикл

let seats = 5;
while (seats > 0) {
  console.log('Есть места');
  // забыли seats-- → цикл никогда не закончится
}

Цикл должен двигаться к завершению: в while меняй переменную из условия, в for проверь, что счётчик идёт к границе, а не от неё (i-- при условии i < 10 бесконечен). Если вкладка зависла, закрой её — код не остановится сам.

Не удаляй элементы из массива в том же цикле, которым его читаешь: индексы сдвинутся, и следующий элемент пропустится. Собери результат в новый массив через filter или перебирай с конца.

Цикл или методы массива

// цикл
const titles = [];
for (const lesson of lessons) {
  if (lesson.done) titles.push(lesson.title);
}

// методы массива
const titles2 = lessons.filter((l) => l.done).map((l) => l.title);

Для стандартных задач — отобрать, преобразовать, найти, посчитать — методы массива короче и не дают ошибиться с индексами. Обычный цикл понятнее, когда внутри несколько разных действий, нужен ранний выход через break или асинхронный код с await.

Цикл и элементы страницы

const list = document.querySelector('.lessons');
const fragment = document.createDocumentFragment();

for (const lesson of lessons) {
  const li = document.createElement('li');
  li.textContent = lesson.title;
  fragment.append(li);
}
list.append(fragment);

Частая задача — вывести массив на страницу. Собирай элементы во фрагмент и добавляй на страницу один раз: браузеру не придётся перерисовывать список на каждом шаге.

for...of или forEach

for...of

Работают break, continue, return и await. Подходит для любых итерируемых объектов.

forEach

Короткая запись для простого действия с каждым элементом массива. Прервать нельзя, await внутри не ждёт.

Частые ошибки

  • Условие никогда не становится ложным, и вкладка зависает.
  • Выходят за границу массива через i <= items.length.
  • Перебирают массив через for...in и получают индексы строками.
  • Удаляют элементы массива в цикле, которым его читают, и пропускают соседей.
  • Пишут цикл с push там, где короче и понятнее map или filter.

Доступность

Когда цикл выводит элементы на страницу, используй списки ul или ol — скринридер объявит количество пунктов. Для длинных списков не перерисовывай всё при каждом изменении: человек со скринридером теряет позицию, если содержимое внезапно заменяется целиком.

Поддержка браузерами

for, while, do...while и for...in работают во всех браузерах. for...of и деструктуризация в нём поддерживаются всеми актуальными браузерами с 2016 года.

Практика

Дан массив цен. Посчитай общую сумму и среднюю цену циклом for...of, пропуская нулевые цены. Затем выведи цены в обратном порядке классическим for и остановись, если встретишь цену больше 10 000.

Показать решение
const prices = [400, 0, 250, 700, 12000, 300];
let total = 0;
let count = 0;

for (const price of prices) {
  if (price === 0) continue;
  total += price;
  count++;
}
console.log(total, total / count);

for (let i = prices.length - 1; i >= 0; i--) {
  if (prices[i] > 10000) break;
  console.log(prices[i]);
}

Открыть песочницу

Частые вопросы

Чем for...of отличается от for...in?

for...of перебирает значения (элементы массива, символы строки), for...in — ключи объекта. Для массивов используй for...of.

Как прервать цикл?

break завершает цикл полностью, continue пропускает текущий шаг. В forEach прервать перебор нельзя — используй for...of или some.

Почему цикл бесконечный?

Условие никогда не становится ложным: переменная из условия не меняется или меняется не в ту сторону. Проверь, что каждый шаг приближает к завершению.

Что выбрать: цикл или map и filter?

Для типовых задач — методы массива, они короче. Цикл — когда нужен break, несколько разных действий или await внутри.

Как перебрать объект?

for (const [key, value] of Object.entries(obj)). Это современнее и безопаснее for...in.

Источники