Методы массивов в JavaScript #
Методы массивов — встроенные функции для перебора, преобразования и агрегации без ручных циклов с индексами. map, filter и reduce — основа обработки корзины, рейтингов и плейлистов в современном JS; важно понимать, какие методы мутируют исходный массив, а какие возвращают новый.
forEach: побочные эффекты #
forEach ничего не возвращает (undefined); подходит для логирования или вызова API:
const cart = [
{ sku: 'kb-01', qty: 1 },
{ sku: 'ms-02', qty: 3 },
]
cart.forEach((line) => {
console.log(line.sku, line.qty)
})
Если нужен новый массив — map, не forEach с push.
map: преобразовать каждый элемент #
const prices = [490, 1290, 199]
const withCurrency = prices.map((p) => `${p} ₽`)
// ["490 ₽", "1290 ₽", "199 ₽"]
const doubledQty = cart.map((line) => ({
...line,
qty: line.qty * 2,
}))
map всегда возвращает массив той же длины, что и исходный.
filter: оставить подходящие #
const inStock = cart.filter((line) => line.qty > 0)
const premium = scores.filter((entry) => entry.points >= 1000)
Пустой результат — валидный массив [], не null.
reduce: свести к одному значению #
const cart = [
{ price: 490, qty: 2 },
{ price: 199, qty: 1 },
]
const subtotal = cart.reduce((sum, line) => sum + line.price * line.qty, 0)
// 1179
Начальное значение аккумулятора (0) задаётся явно — так безопаснее для пустого массива.
Собрать объект из списка:
const tracks = [
{ id: 't1', bpm: 120 },
{ id: 't2', bpm: 90 },
]
const bpmById = tracks.reduce((acc, track) => {
acc[track.id] = track.bpm
return acc
}, {})
// { t1: 120, t2: 90 }
find и findIndex #
const line = cart.find((item) => item.sku === 'ms-02')
// объект или undefined
const idx = playlist.findIndex((t) => t.id === 'focus-03')
// индекс или -1
find останавливается на первом совпадении.
some и every #
const hasExpress = deliveryOptions.some((o) => o.type === 'express')
const allPaid = orders.every((o) => o.status === 'paid')
some — «хотя бы один»; every — «все без исключения». Оба коротко-замыкаются: как только результат ясен, перебор прекращается.
Цепочки методов #
const result = cart
.filter((line) => line.qty > 0)
.map((line) => line.price * line.qty)
.reduce((sum, amount) => sum + amount, 0)
Читается сверху вниз: отфильтровали → посчитали строки → сложили.
sort: сортировка и ловушка мутации #
sort меняет исходный массив и возвращает ссылку на него:
const nums = [30, 10, 20]
nums.sort((a, b) => a - b)
nums // [10, 20, 30]
Без компаратора элементы приводятся к строкам:
[10, 9, 80].sort() // [10, 80, 9] — лексикографически!
Сортировка по числу:
scores.sort((a, b) => b.points - a.points) // по убыванию очков
Сортировка по строке с локалью:
tracks.sort((a, b) => a.title.localeCompare(b.title, 'ru'))
Безопасная сортировка: копия #
const original = [{ score: 88 }, { score: 120 }, { score: 95 }]
const ranked = [...original].sort((a, b) => b.score - a.score)
// ranked отсортирован, original — в прежнем порядке
const ranked2 = original.slice().sort((a, b) => b.score - a.score)
Паттерн «sort copy» в JSFlow: никогда не сортируйте состояние React/хранилища напрямую, если порядок исходника должен сохраниться.
Другие полезные методы #
const ids = ['a', 'b', 'a', 'c']
ids.includes('b') // true
const flat = [1, [2, 3], [[4]]].flat(2) // [1, 2, 3, 4]
const merged = [1, 2].concat([3, 4]) // [1, 2, 3, 4] — альтернатива spread
playlist.slice(1, 3) // копия участка без мутации
splice — мутирующее вырезание/вставка; в прикладном коде чаще заменяют на filter + spread.
Мутирующие vs немутирующие #
| Мутирует массив | Возвращает новый |
|---|---|
push, pop, shift, unshift | map, filter, slice, concat |
sort, reverse, splice, fill | toSorted, toReversed (ES2023, если доступны) |
В средах с toSorted:
const order = [3, 1, 2]
const sorted = order.toSorted((a, b) => a - b)
order // [3, 1, 2] — без изменений
Иначе — [...arr].sort(...).
Практика: чекаут #
const cart = [
{ sku: 'kb-01', price: 4900, qty: 1, category: 'gear' },
{ sku: 'ms-02', price: 1290, qty: 0, category: 'gear' },
{ sku: 'sub-01', price: 299, qty: 1, category: 'digital' },
]
function checkoutLines(cart) {
return cart
.filter((line) => line.qty > 0)
.map((line) => ({
sku: line.sku,
amount: line.price * line.qty,
}))
}
function cartTotal(cart) {
return checkoutLines(cart).reduce((sum, line) => sum + line.amount, 0)
}
checkoutLines(cart)
// [{ sku: 'kb-01', amount: 4900 }, { sku: 'sub-01', amount: 299 }]
cartTotal(cart) // 5199
function topScores(entries, limit = 3) {
return [...entries]
.sort((a, b) => b.score - a.score)
.slice(0, limit)
}
topScores(
[
{ name: 'Аня', score: 120 },
{ name: 'Борис', score: 200 },
{ name: 'Вика', score: 150 },
],
2
)
// [{ name: 'Борис', ... }, { name: 'Вика', ... }]
Практика: плейлист #
const playlist = [
{ id: 1, title: 'Warmup', duration: 180 },
{ id: 2, title: 'Focus', duration: 240 },
{ id: 3, title: 'Cooldown', duration: 120 },
]
const totalSec = playlist.reduce((sum, t) => sum + t.duration, 0)
const longTracks = playlist.filter((t) => t.duration >= 200)
const titles = playlist.map((t) => t.title)
Итог #
map/filter/reduce— три кита преобразования данных; задавайте начальное значение вreduce.find,some,everyостанавливаются раньше, чем полный перебор, когда ответ уже известен.sortмутирует массив; для UI-состояния сортируйте копию:[...arr].sort(...)илиtoSorted.- Числовая сортировка требует компаратора
(a, b) => a - b, иначе сравнение строковое. - Цепочки методов читаются лучше вложенных циклов, если каждый шаг делает одну вещь.