Как перебирать массив в JavaScript: Топ ошибок и как их избежать
Вы когда-нибудь писали код, который должен был просто пройтись по списку дел или товаров в корзине, а вместо этого выдавал undefined, бесконечный цикл или, что еще хуже, тихо портил данные? Знакомая боль. Вопрос "How to loop through the items of an array in JavaScript?" — один из самых популярных на Stack Overflow. Кажется, что может быть проще? Но дьявол, как всегда, кроется в деталях.
Сегодня мы не просто разберем, как перебирать массив в JavaScript. Мы заглянем под капот самых частых граблей, на которые наступают новички (и не только). Вы узнаете, почему for...in может вас подставить, чем forEach отличается от map, и как не словить бесконечный цикл в for.
Готовы? Погнали чинить баги и писать красивый код.
Ошибка №1: Использование for...in для массивов
Это, пожалуй, самый коварный новичковый трепет. Разработчик видит слово for и слово in и думает: "О, это же для перебора всего подряд!". Технически это правда. Но для массивов это катастрофа.
Цикл for...in предназначен для перебора свойств объекта, а не элементов массива. Да, массив в JS — это тоже объект, но его индексы — это всего лишь строковые ключи ("0", "1", "2").
Вот в чем подвох:
- Порядок не гарантирован. Хотя обычно браузеры соблюдают порядок, полагаться на это нельзя.
- Он перебирает всё прототипное наследование. Если вы или какая-то библиотека добавили метод в
Array.prototype(например,Array.prototype.myCustomMethod = function() {}),for...inподхватит и его.
Пример кода (как делать НЕ надо):
const arr = [10, 20, 30];
Array.prototype.customMethod = () => {};
for (let index in arr) {
console.log(arr[index]); // Выведет: 10, 20, 30, ƒ () => {}
}
Видите? В конце вылезла наша кастомная функция. Код сломался.
Как правильно:
Используйте for...of (для значений) или классический for (для индексов). for...of перебирает только значения и игнорирует прототип.
// Правильно и безопасно
for (let value of arr) {
console.log(value); // 10, 20, 30
}
Ошибка №2: Забыть про колбэк в forEach
arr.forEach(callback) — это элегантный способ перебора массива в JavaScript. Но многие путают его с map или filter. Главная ошибка — думать, что forEach что-то возвращает. Он возвращает undefined.
Вторая распространенная ошибка — забывать про второй и третий аргументы колбэка (индекс и сам массив). Иногда это критично.
Пример ошибки:
const numbers = [1, 2, 3];
const doubled = numbers.forEach(num => num * 2);
console.log(doubled); // undefined! Ошибка.
Как правильно:
Если вам нужно создать новый массив на основе старого — используйте map(). Если нужно только выполнить действие (например, вывести в консоль или отправить данные) — используйте forEach().
// Для создания нового массива
const doubled = numbers.map(num => num * 2); // [2, 4, 6]
// Для побочных эффектов
numbers.forEach((num, index) => {
console.log(`Элемент ${index}: ${num}`);
});
Ошибка №3: Бесконечный цикл в for
Классика жанра. Вы пишете for (let i = 0; i < arr.length; i++), но забываете про инкремент i++ или, что еще хуже, меняете массив внутри цикла.
Допустим, вы фильтруете массив на лету, удаляя элементы с помощью splice(). Если вы не скорректируете индекс, вы пропустите элементы.
Пример кода (как делать НЕ надо):
let arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
if (arr[i] % 2 === 0) {
arr.splice(i, 1); // Удаляем элемент
}
}
console.log(arr); // [1, 3, 5]? Нет. Будет [1, 3, 4, 5] — элемент 4 пропущен!
Почему? Потому что после удаления элемента с индексом 1 (число 2), элемент "3" смещается на индекс 1, а цикл уже переходит к индексу 2. Мы пропустили проверку.
Как правильно:
Самый надежный способ — идти по массиву с конца.
let arr = [1, 2, 3, 4, 5];
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i] % 2 === 0) {
arr.splice(i, 1);
}
}
console.log(arr); // [1, 3, 5] — теперь правильно!
Или используйте filter(), который создает новый массив и не мутирует исходный (если вы этого не хотите).
Ошибка №4: Путаница между map() и forEach()
Эта ошибка тесно связана с пунктом 2. Многие новички, изучая методы массивов в JavaScript, не понимают разницы между итерацией и трансформацией.
forEach()— это "сделай что-то с каждым элементом". Результат работы колбэка игнорируется.map()— это "создай новый массив, применив функцию к каждому элементу". Результат каждого вызова колбэка собирается в новый массив.
Если вы пишете arr.map(x => console.log(x)) — вы делаете это неправильно. Вы создаете новый массив из undefined (потому что console.log ничего не возвращает) и тратите память.
Как правильно:
Всегда задавайте себе вопрос: "Мне нужен новый массив на выходе?". Если да — map, filter, reduce. Если нет — forEach.