Avatar ·

Как правильно перебрать массив в JavaScript: forEach, for, for...of и другие способы

Всем привет! Я только начал изучать JavaScript и никак не могу разобраться с перебором массивов. У меня есть простой массив с именами пользователей:
const users = ['Анна', 'Иван', 'Мария', 'Петр'];
Мне нужно вывести каждое имя в консоль. Я пробовал использовать обычный цикл for, но у меня постоянно путаница с индексами и длиной массива. Например, я пишу:
for (let i = 0; i <= users.length; i++) {
    console.log(users[i]);
}
И в конце получаю undefined. Почему так происходит? Я слышал, что есть метод forEach, но не понимаю, как он работает и чем отличается от обычного for. Ещё видел какие-то for...of и for...in, но совсем запутался. Подскажите, пожалуйста, какой способ лучше использовать для перебора массива в современных проектах? Мне нужно не просто вывести элементы, но иногда и изменить их, или создать новый массив на основе существующего. Буду благодарен за подробное объяснение с примерами для новичков. <<>>

Основные способы перебора массива в JavaScript

Давайте разберём все популярные способы итерации по массиву, начиная с вашей ошибки.

1. Классический цикл for

Ваша ошибка — использование <= вместо <. Индексы массива начинаются с 0, поэтому последний элемент имеет индекс length - 1. Правильно:

const users = ['Анна', 'Иван', 'Мария', 'Петр'];

for (let i = 0; i < users.length; i++) {
    console.log(users[i]); // Анна, Иван, Мария, Петр
}

Этот способ даёт полный контроль: вы можете менять элементы по индексу, прерывать цикл через break, использовать continue.

2. Метод forEach() — современный стандарт

Самый читаемый способ для простого перебора. Вызывается прямо на массиве и принимает функцию-колбэк:

users.forEach(function(user, index) {
    console.log(`${index}: ${user}`);
});
// Вывод: 0: Анна, 1: Иван, 2: Мария, 3: Петр

Стрелочная функция делает код ещё короче:

users.forEach((user, index) => console.log(`${index}: ${user}`));

Важно: forEach не возвращает новый массив, его нельзя прервать через break, и он не работает с await внутри колбэка (в таких случаях используйте for...of).

3. Цикл for...of — для работы с элементами

Появился в ES6, идеален, когда нужны только значения, а не индексы:

for (const user of users) {
    console.log(user); // Анна, Иван, Мария, Петр
}

Можно использовать break и continue, а также await внутри цикла. Если нужен индекс, используйте entries():

for (const [index, user] of users.entries()) {
    console.log(`${index}: ${user}`);
}

4. for...in — НЕ используйте для массивов!

Этот цикл предназначен для объектов. Он перебирает не только индексы, но и все перечисляемые свойства, включая унаследованные. Результат непредсказуем:

for (const key in users) {
    console.log(key); // "0", "1", "2", "3" (строки, а не числа!)
}

Когда что использовать?

  • Нужен индекс и/или изменение элементов — классический for или forEach с индексом.
  • Просто перебрать значения — for...of или forEach.
  • Создать новый массив — map() (см. ниже).
  • Асинхронные операции внутри цикла — for...of с await.

Продвинутые методы (функциональный подход)

Современный JavaScript предлагает методы, которые возвращают новые массивы:

// map — создаёт новый массив, преобразуя каждый элемент
const upperUsers = users.map(user => user.toUpperCase());
// ['АННА', 'ИВАН', 'МАРИЯ', 'ПЕТР']

// filter — фильтрует элементы по условию
const longNames = users.filter(user => user.length > 3);
// ['Анна', 'Мария', 'Петр']

// reduce — свёртка массива в одно значение
const allNames = users.reduce((acc, user) => acc + ' ' + user, 'Пользователи:');
// 'Пользователи: Анна Иван Мария Петр'

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

<<>>

Альтернативный подход: методы map(), filter() и reduce()

Если ваша цель — не просто перебрать массив, а преобразовать его или получить новую структуру данных, функциональные методы часто удобнее циклов.

Рассмотрим реальную задачу: у нас есть массив объектов с пользователями, и нужно получить массив имён только совершеннолетних пользователей:

const users = [
    { name: 'Анна', age: 17 },
    { name: 'Иван', age: 25 },
    { name: 'Мария', age: 19 },
    { name: 'Петр', age: 16 }
];

// Решение через цепочку методов
const adultNames = users
    .filter(user => user.age >= 18)  // оставляем только взрослых
    .map(user => user.name);         // забираем только имена

console.log(adultNames); // ['Иван', 'Мария']

Такой код читается как последовательность шагов: «отфильтруй, затем преобразуй». Он не мутирует исходный массив, что предотвращает побочные эффекты.

Когда это не подходит:

  • Если нужно прервать цикл досрочно (нет аналога break).
  • Если внутри колбэка есть асинхронные операции (лучше for...of с await).
  • Если производительность критична (для гигантских массивов классический for быстрее).

В остальных случаях функциональный подход делает код чище и предсказуемее. Рекомендую освоить map, filter и reduce — это основа стиля современного JavaScript.