Гид по технологиям

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

6 min read JavaScript Обновлено 19 Dec 2025
Циклы в JavaScript — перебор массивов
Циклы в JavaScript — перебор массивов

Редактор кода с фрагментами 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
// 1

for…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
// 6

while полезен, когда шаг цикла не фиксирован или условие основано на внешнем состоянии.

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)

  1. Определите, нужен ли индекс или порядок обратный.
  2. Определите, возможна ли досрочная остановка цикла.
  3. Решите, есть ли асинхронные операции с await.
  4. Выберите наиболее читабельный вариант, учитывая производительность.
  5. Напишите тест, покрывающий граничные случаи (пустой массив, null/undefined в коллекции, изменение длины во время перебора).

Глоссарий в одну строку

  • Итерация — последовательный проход по элементам коллекции.
  • Итерируемый — объект, поддерживающий протокол итерации (Symbol.iterator).
  • Коллбек — функция, передаваемая в другой метод и вызываемая в процессе выполнения.

Примеры тест-кейсов (приёмка)

  • Пустой массив — цикл ничего не выводит/не ломается.
  • Массив с одним элементом — цикл выполняет одно действие.
  • Удаление элемента во время перебора — соответствует ожидаемому поведению (тестировать).
  • Асинхронный перебор с await — выполняется последовательно, если используется for…of.

Итог

Циклы — базовый инструмент для работы с массивами в JavaScript. Выбирайте тот, который лучше соответствует требованиям: контроль, читабельность, асинхронность и безопасность. Комбинируйте циклы с методами массива (map/filter/reduce) для более декларативного и чистого кода.

Кратко: для значений — for…of, для индексов и контроля — for, для декларативных преобразований — map/filter/reduce, избегайте for…in для массивов и forEach при необходимости await.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как запустить Epic и GOG на Steam Deck
Steam Deck

Как запустить Epic и GOG на Steam Deck

Исправить ошибку 0x80042306 в Windows
Windows

Исправить ошибку 0x80042306 в Windows

Как разблокировать пользователя в Instagram
Социальные сети

Как разблокировать пользователя в Instagram

Защита от 6 типов нарушений электропитания
Электрика

Защита от 6 типов нарушений электропитания

Как сохранить домашний интернет при отключении света
Домашний интернет

Как сохранить домашний интернет при отключении света

Greenfoot — визуальное программирование на Java
Программирование

Greenfoot — визуальное программирование на Java