Методы массивов JavaScript: шпаргалка
У массивов в JavaScript больше 30 встроенных методов, но в ежедневной работе хватает десятка. Главное, что нужно понять сразу: одни методы возвращают новый массив и не трогают исходный, другие меняют массив на месте. Перепутаете - получите баг, который трудно найти, особенно в React, где состояние нельзя мутировать.
| Метод | Что делает | Возвращает | Меняет исходный |
|---|---|---|---|
forEach | Выполняет функцию для каждого элемента | undefined | Нет |
map | Преобразует каждый элемент | Новый массив той же длины | Нет |
filter | Отбирает элементы по условию | Новый массив | Нет |
reduce | Сводит массив к одному значению | Любое значение | Нет |
find / findIndex | Ищет первый подходящий элемент | Элемент или undefined / индекс или -1 | Нет |
some / every | Проверяет условие хотя бы для одного / для всех | true или false | Нет |
includes | Есть ли значение в массиве | true или false | Нет |
slice | Копирует часть массива | Новый массив | Нет |
sort, reverse | Сортирует, разворачивает | Тот же массив | Да |
push, pop, shift, unshift | Добавляет и удаляет с краев | Длину или удаленный элемент | Да |
splice | Удаляет и вставляет в середине | Массив удаленных | Да |
Все примеры ниже можно запустить в консоли браузера (F12 - Console) или в Node.js. Для них возьмем один массив товаров:
const products = [
{ name: "Ноутбук", price: 85000, category: "tech", inStock: true },
{ name: "Мышь", price: 1200, category: "tech", inStock: false },
{ name: "Кружка", price: 600, category: "home", inStock: true },
{ name: "Лампа", price: 3400, category: "home", inStock: true },
];forEach: перебор массива
forEach вызывает функцию для каждого элемента и ничего не возвращает. Функция получает три аргумента: элемент, его индекс и сам массив. Обычно нужны первые два.
products.forEach((item, i) => {
console.log(`${i + 1}. ${item.name} - ${item.price} руб.`);
});
Используйте forEach, когда нужен побочный эффект: вывести в консоль, отправить запрос, изменить DOM. Если вы хотите получить новый массив, берите map или filter.
Два ограничения. Из forEach нельзя выйти досрочно через break, а return внутри только пропускает текущую итерацию. Если нужен ранний выход, используйте обычный цикл for...of или методы some и find, которые останавливаются сами. И forEach не ждет асинхронные функции: arr.forEach(async x => await save(x)) запустит все запросы разом и не дождется ни одного. Для последовательных запросов пишите for...of с await, для параллельных - await Promise.all(arr.map(save)).
map: преобразование каждого элемента
map создает новый массив, в котором каждый элемент - результат вашей функции. Длина результата всегда равна длине исходного массива.
const names = products.map(p => p.name);
// ['Ноутбук', 'Мышь', 'Кружка', 'Лампа']
const withVat = products.map(p => ({ ...p, price: Math.round(p.price * 1.2) }));
// исходный products не изменился
Обратите внимание на скобки вокруг объекта во втором примере. Без них p => { ...p } JavaScript воспримет фигурные скобки как тело функции, и вы получите синтаксическую ошибку или массив из undefined. Та же беда случается, если в стрелочной функции с фигурными скобками забыть return:
[1, 2, 3].map(x => { x * 2 }); // [undefined, undefined, undefined]
[1, 2, 3].map(x => { return x * 2; }); // [2, 4, 6]
[1, 2, 3].map(x => x * 2); // [2, 4, 6]
В React map - основной способ вывести список: items.map(item => <li key={item.id}>{item.name}</li>). Атрибут key с уникальным значением обязателен, индекс массива в качестве ключа подходит только для статичных списков.
Ловушка с parseInt
Классический вопрос с собеседований: что вернет ["1", "2", "3"].map(parseInt)? Ответ [1, NaN, NaN]. map передает в функцию второй аргумент - индекс, а parseInt принимает его как систему счисления. Пишите явно: .map(s => parseInt(s, 10)) или .map(Number).
filter: отбор элементов по условию
filter возвращает новый массив только из тех элементов, для которых функция вернула истинное значение.
const available = products.filter(p => p.inStock);
const cheapHome = products.filter(p => p.category === "home" && p.price < 1000);
// [{ name: 'Кружка', ... }]
const clean = [0, "a", null, "b", undefined, ""].filter(Boolean);
// ['a', 'b']
Последний прием убирает из массива все ложные значения: 0, пустую строку, null, undefined, NaN. Осторожно, если ноль - нормальное значение, он тоже пропадет.
Если нужен только первый подходящий элемент, а не все, используйте find: он остановится на первом совпадении и вернет сам объект, а не массив из одного элемента. Для удаления элемента из массива в React-состоянии filter - стандартный способ: setItems(items.filter(i => i.id !== id)).
reduce: свести массив к одному значению
reduce - самый гибкий и самый пугающий метод. Он проходит по массиву и копит результат в аккумуляторе. Первый аргумент - функция (acc, item) => новыйAcc, второй - начальное значение аккумулятора.
const total = products.reduce((sum, p) => sum + p.price, 0);
// 90200
const byCategory = products.reduce((acc, p) => {
acc[p.category] = (acc[p.category] || 0) + 1;
return acc;
}, {});
// { tech: 2, home: 2 }
const maxPrice = products.reduce((max, p) => Math.max(max, p.price), -Infinity);
Как это работает пошагово на сумме:
- Аккумулятор
sumравен начальному значению 0. - Для ноутбука функция возвращает 0 + 85000, это новое значение sum.
- Для мыши: 85000 + 1200 = 86200.
- Для кружки и лампы: 86800, затем 90200.
- Последнее возвращенное значение становится результатом reduce.
Всегда передавайте начальное значение вторым аргументом. Без него reduce возьмет первый элемент массива как аккумулятор: на массиве объектов вы получите строку вида "[object Object]1200..." вместо суммы, а на пустом массиве - TypeError. И не забывайте return acc в функциях с фигурными скобками, это ошибка номер один в reduce.
Для группировки объектов в современных браузерах и Node.js 21+ есть готовый Object.groupBy(products, p => p.category), он часто читается проще, чем reduce.
Цепочки map, filter и reduce
Методы, которые возвращают массив, можно вызывать друг за другом. Получается читаемый конвейер обработки данных.
const sumInStock = products
.filter(p => p.inStock) // только в наличии
.map(p => p.price) // берем цены
.reduce((s, x) => s + x, 0); // складываем
// 89000
Каждый шаг создает промежуточный массив. Для списков из сотен и тысяч элементов это незаметно. Если массив на миллионы элементов и код в горячем цикле, один reduce или обычный цикл будет быстрее, но начинать стоит с читаемого варианта и оптимизировать после замера.
Похожие задачи часто приходят с сервера в формате JSON: вы получаете массив объектов из API и фильтруете его перед выводом. Как устроены такие ответы, описано в статьях что такое API и JSON: что это.
find, some, every, sort и новые методы
products.find(p => p.price > 3000); // { name: 'Ноутбук', ... }
products.findIndex(p => p.name === "Лампа"); // 3
products.some(p => !p.inStock); // true - есть хоть один не в наличии
products.every(p => p.price > 500); // true
[1, 2, 3].includes(2); // true
[[1, 2], [3]].flat(); // [1, 2, 3]
products.at(-1).name; // 'Лампа' - последний элемент
Сортировка без сюрпризов
sort без аргументов сравнивает элементы как строки: [10, 9, 1].sort() даст [1, 10, 9]. Для чисел передайте функцию сравнения (a, b) => a - b, для русских строк используйте localeCompare. И помните, что sort меняет исходный массив.
const byPrice = [...products].sort((a, b) => a.price - b.price);
const byName = products.toSorted((a, b) => a.name.localeCompare(b.name));
Методы toSorted, toReversed, toSpliced и with появились в стандарте ES2023. Они делают то же, что sort, reverse и splice, но возвращают копию и не мутируют исходный массив. Поддерживаются в актуальных браузерах и Node.js 20+. Для старых окружений используйте копию через spread, как в первой строке.
Какой метод выбрать: краткая памятка
| Задача | Метод |
|---|---|
| Получить массив того же размера с измененными элементами | map |
| Оставить часть элементов | filter |
| Посчитать сумму, среднее, собрать объект | reduce |
| Найти один элемент | find |
| Проверить, есть ли подходящий | some |
| Проверить, что подходят все | every |
| Сделать что-то для каждого без результата | forEach или for...of |
| Отсортировать без изменения исходного | toSorted или [...arr].sort() |
Если только начинаете путь во фронтенде и выбираете направление, посмотрите сравнение frontend или backend. Поиск по строкам внутри массивов удобно делать регулярками, их разбор в статье регулярные выражения: шпаргалка. Остальные материалы - в теге JavaScript.
