Циклы в JavaScript: все способы перебора массивов

Зачем знать разные виды циклов
Коротко: разные циклы дают разные возможности — доступ к индексу, возможность прерывания (break/continue), работа с асинхронным кодом, лаконичность или простая читабельность. Выбор влияет на читаемость, производительность и безопасность (неперебор через унаследованные свойства).
Ключевые варианты использования (интуитивно):
- Нужен индекс и контроль последовательности — стандартный for.
- Нужен лаконичный перебор значений — for…of или forEach.
- Нужно получить ключи/имена свойств объекта — for…in (только для перечисляемых свойств объекта, осторожно с прототипом).
- Нужна возможность прерывать цикл — for / while / for…of (forEach нельзя прервать через break).
Общие правила и термины
- Массив — упорядоченная коллекция элементов, индексируемая от 0.
- Индекс — позиция элемента в массиве (0, 1, 2…).
- Итерируемый объект — объект, который поддерживает протокол итерации (например, массивы, строки, Map, Set).
Инкрементальный и декрементальный for
Классический for — самый универсальный: позволяет задавать начальное значение, условие и шаг (увеличение/уменьшение).
Простой инкрементный перебор:
const anArray = [1, 3, 5, 6];
for (let i = 0; i < anArray.length; i++) {
console.log(anArray[i]);
}
// Output:
// 1
// 3
// 5
// 6Операция с каждым элементом:
const anArray = [1, 3, 5, 6];
for (let i = 0; i < anArray.length; i++) {
console.log(`5 x ${anArray[i]} = ${anArray[i] * 5}`);
}
// Output:
// 5 x 1 = 5
// 5 x 3 = 15
// 5 x 5 = 25
// 5 x 6 = 30Декрементный перебор (обход в обратном порядке):
const anArray = [1, 3, 5, 6];
for (let i = anArray.length - 1; i >= 0; i--) {
console.log(`5 x ${anArray[i]} = ${anArray[i] * 5}`);
}
// Output:
// 5 x 6 = 30
// 5 x 5 = 25
// 5 x 3 = 15
// 5 x 1 = 5Важно: проверка i >= 0 позволяет корректно остановиться на первом элементе (индекс 0).
forEach — лаконичный перебор значений
forEach принимает коллбек и применяет его к каждому элементу. Он не возвращает новый массив и не поддерживает break/continue — это ключевое отличие.
Синтаксис и пример:
const anArray = [1, 3, 5, 6];
anArray.forEach(x => {
console.log(x);
});
// Output:
// 1
// 3
// 5
// 6Пример с операцией:
const anArray = [1, 3, 5, 6];
anArray.forEach(x => {
console.log(`5 x ${x} = ${x * 5}`);
});
// Output:
// 5 x 1 = 5
// 5 x 3 = 15
// 5 x 5 = 25
// 5 x 6 = 30Когда не использовать forEach:
- Когда нужно прервать цикл досрочно (break).
- Когда нужен асинхронный цикл с await (forEach не поддерживает await внутри коллбэка правильно).
for…in — перебор ключей (с осторожностью)
for…in перебирает перечисляемые свойства объекта и возвращает ключи (индексы для массива). Для массивов он возвращает строки с индексами: “0”, “1” и т.д. Обычно лучше использовать его для объектов, а не для массивов.
Пример получения индексов:
const anArray = [1, 3, 5, 6];
for (let i in anArray) {
console.log(i);
}
// Output:
// 0
// 1
// 2
// 3Чтобы получить значения по индексам:
const anArray = [1, 3, 5, 6];
for (let i in anArray) {
console.log(anArray[i]);
}
// Output:
// 1
// 3
// 5
// 6Предупреждение: for…in перебирает также добавленные свойства в прототипе и строковые ключи — для массивов это может привести к неожиданностям. Для обхода массивов предпочтительнее for, for…of или методы массивов.
for…of — перебор значений и удобство
for…of итеративно возвращает значения итерируемого объекта (массивы, строки, Map, Set и т. д.). Он более читаем для перебора значений, чем for…in, и поддерживает break/continue.
Пример:
const anArray = [1, 3, 5, 6];
for (const value of anArray) {
console.log(value);
}
// Output:
// 1
// 3
// 5
// 6Обход в обратном порядке (с индексацией):
const anArray = [1, 3, 5, 6];
let v = anArray.length - 1;
for (const _ of anArray) {
console.log(anArray[v]);
v -= 1;
}
// Output:
// 6
// 5
// 3
// 1for…of удобен, когда нужен доступ к значениям и возможность прерывания цикла.
while — условный цикл
while выполняется пока условие истинно. Его легко использовать как простую петлю с предварительной проверкой условия.
Пример бесконечного цикла (не делайте так в реальном коде без выхода):
let i = 0;
while (i < 10) {
console.log(4);
// Без изменения i это будет бесконечный цикл
i += 1; // чтобы избежать бесконечности
}Итерация массива при помощи while:
const anArray = [1, 3, 5, 6];
let i = 0;
while (i < anArray.length) {
console.log(anArray[i]);
i += 1;
}
// Output:
// 1
// 3
// 5
// 6while полезен, когда шаг цикла не фиксирован или условие основано на внешнем состоянии.
do…while — цикл с гарантированным одним выполнением
do…while сначала выполняет тело, а затем проверяет условие. Полезно, если нужно гарантировать хотя бы одно выполнение.
Пример:
const anArray = [1, 3, 5, 6];
let i = 0;
do {
console.log(anArray[i]);
i += 1;
} while (i < anArray.length);
// Output:
// 1
// 3
// 5
// 6Когда какой цикл выбирать — простая эвристика
- Нужен индекс или двойной цикл: for.
- Нужна максимально простая запись без прерываний: forEach.
- Нужно значение + возможность break/continue: for…of.
- Перебор свойств объектов: for…in (с проверкой hasOwnProperty).
- Условный цикл с непредсказуемым шагом: while.
- Нужно хотя бы одно выполнение перед проверкой: do…while.
Важно: для асинхронных операций используйте for…of с await (например, for await…of для асинхронных итераторов). forEach не совместим с await внутри коллбэка.
Альтернативы циклам: методы массивов и функциональный стиль
Часто вместо явного цикла используют методы массивов:
- map — трансформирует и возвращает новый массив.
- filter — фильтрует элементы.
- reduce — аккумулирует значение.
- some/every — проверяют предикаты.
Пример map:
const arr = [1, 3, 5, 6];
const doubled = arr.map(x => x * 2);
console.log(doubled);
// Output: [2, 6, 10, 12]Преимущество: декларативность и иммутабельность (возвращается новый массив). Недостаток: возможны лишние аллокации при больших объёмах данных (иногда критично в tight loops).
Примеры ошибок и когда подход ломается
- Использовать for…in для массива, если кто-то добавил свойства в Array.prototype — получите неожиданные ключи.
- Использовать forEach для асинхронного кода с await — промисы не будут обрабатываться в нужном порядке.
- Модификация массива во время перебора (удаление/вставка) может привести к пропуску или повторному выполнению элементов при инкрементных циклах.
Пример потенциальной ошибки:
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 2) arr.splice(i, 1); // удаление сдвинет индексы
console.log(arr[i]);
}Решение: итерировать в обратном порядке при необходимости удаления, либо сначала собрать индексы для удаления, либо использовать filter.
Шпаргалка — что запомнить
- for: универсален, есть index, можно break/continue.
- forEach: лаконично, нет break, нет await.
- for…in: ключи объекта, осторожно с прототипом.
- for…of: значения итератора, можно break/continue, совместим с await.
- while: гибкое условие.
- do…while: гарантированное первое выполнение.
Небольшой чеклист для разработчика и код-ревьюера
Для разработчика:
- Есть ли необходимость в индексе? Если да — for или forEach с вторым параметром (index).
- Планируется ли прерывание цикла? Если да — не использовать forEach.
- Могут ли изменять массив во время перебора? Если да — обход в обратном порядке или копировать массив.
Для код-ревьюера:
- Проверить использование for…in для массивов.
- Проверить использование await внутри forEach.
- Оценить округлость аллокаций — можно ли заменить несколько проходов map/filter на один reduce.
Краткая методология выбора (mini-playbook)
- Определите, нужен ли индекс или порядок обратный.
- Определите, возможна ли досрочная остановка цикла.
- Решите, есть ли асинхронные операции с await.
- Выберите наиболее читабельный вариант, учитывая производительность.
- Напишите тест, покрывающий граничные случаи (пустой массив, null/undefined в коллекции, изменение длины во время перебора).
Глоссарий в одну строку
- Итерация — последовательный проход по элементам коллекции.
- Итерируемый — объект, поддерживающий протокол итерации (Symbol.iterator).
- Коллбек — функция, передаваемая в другой метод и вызываемая в процессе выполнения.
Примеры тест-кейсов (приёмка)
- Пустой массив — цикл ничего не выводит/не ломается.
- Массив с одним элементом — цикл выполняет одно действие.
- Удаление элемента во время перебора — соответствует ожидаемому поведению (тестировать).
- Асинхронный перебор с await — выполняется последовательно, если используется for…of.
Итог
Циклы — базовый инструмент для работы с массивами в JavaScript. Выбирайте тот, который лучше соответствует требованиям: контроль, читабельность, асинхронность и безопасность. Комбинируйте циклы с методами массива (map/filter/reduce) для более декларативного и чистого кода.
Кратко: для значений — for…of, для индексов и контроля — for, для декларативных преобразований — map/filter/reduce, избегайте for…in для массивов и forEach при необходимости await.
Похожие материалы
Как запустить Epic и GOG на Steam Deck
Исправить ошибку 0x80042306 в Windows
Как разблокировать пользователя в Instagram
Защита от 6 типов нарушений электропитания
Как сохранить домашний интернет при отключении света