Урок · JavaScript
Циклы: повторяем действия без копирования
Цикл в 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); // 650continue пропускает остаток текущего шага и переходит к следующему. 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 |
| Нужен номер шага или шаг не 1 | for |
| Число повторов неизвестно | 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
Работают break, continue, return и await. Подходит для любых итерируемых объектов.
Короткая запись для простого действия с каждым элементом массива. Прервать нельзя, 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.