Методи масиву vs цикли
1. Коротко: у чому суть
Методи масивів (map, filter, forEach, reduce і т.д.) - це абстракції над циклами, написані всередині рушія JavaScript.
Вони зручніші, коротші й зрозуміліші,
але мають додаткові накладні витрати, через що можуть бути повільнішими за прості цикли (for, for…of, while) - особливо на великих масивах.
2. Що відбувається "під капотом"
Коли ти пишеш:
arr.map(x => x * 2);рушій робить приблизно таке:
- Перевіряє, що
arrдійсно масив. - Створює новий масив для результатів.
- На кожній ітерації:
- викликає callback-функцію;
- передає їй три аргументи
(element, index, array); - зберігає результат у новий масив.
- Повертає підсумковий масив.
Це купа кроків, які додають накладні витрати,
порівняно зі звичайним for-циклом, де ти просто виконуєш інструкції без обгорток і перевірок.
3. Чому саме повільніше
| Причина | Що відбувається |
|---|---|
| Callback-функція | На кожній ітерації викликається нова функція -> зайві виклики стека |
| Створення нового масиву | map, filter, slice, concat створюють копії (дод. пам'ять) |
| Перевірки й контекст | Метод проходить валідації (довжина, holes, прототип, тип) |
| Неоптимізовані замикання | Якщо колбек захоплює зовнішні змінні - ще більше накладних витрат |
| Функціональні принципи | Ці методи "чисті" і не мутують дані -> потрібно більше алокацій |
| Цикли простіше оптимізувати JIT-компілятору | Рушій (V8, SpiderMonkey) швидше оптимізує "голий" for |
4. Приклад порівняння
const arr = Array.from({ length: 1_000_000 }, (_, i) => i);
// map
console.time('map');
const doubled1 = arr.map(x => x * 2);
console.timeEnd('map');
// for
console.time('for');
const doubled2 = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
doubled2[i] = arr[i] * 2;
}
console.timeEnd('for');На більшості рушіїв результат буде приблизно такий:
map: 30-60 ms
for: 10-20 msРізниця невелика на малих даних,
але при мільйонах елементів - у 2-5 разів швидше з for.
5. Чому це особливо помітно в "гарячих" ділянках коду
Якщо цикл виконується мільйони разів (рендеринг, сортування, агрегації даних, парсинг) - накладні витрати колбеків починають "коштувати дорого".
У таких місцях:
- кожен виклик колбека = новий запис у call stack;
- зайві алокації = більше роботи GC (garbage collector);
- дод. аргументи (
index,array) -> більше об'єктів у пам'яті.
6. Коли це не має значення
Для більшості бізнес-задач (списки, фільтри, маппінг до 10k елементів):
- різниця між
forіmap- частки мілісекунди; - читабельність і чистота коду важливіші.
Тому в React/Vue-коді використовують .map() для JSX, бо це декларативно й зрозуміло:
{items.map(item => <Card key={item.id} {...item} />)}Але якщо у тебе масив на мільйони елементів або цикл у "гарячому місці" (рендеринг, обробка бінарних даних, canvas, парсер) - краще використовувати:
for (let i = 0; i < n; i++) ...7. Що реально швидше (за порядком)
| Цикл | Швидкість | Особливості |
|---|---|---|
for (let i = 0; i < n; i++) | Найшвидший | Немає перевірок, inline, передбачуваний |
for...of | Швидкий, але з ітератором | Трохи накладних витрат |
while | Приблизно так само | Майже як for, залежить від рушія |
forEach() | Повільніший (callback) | Не повертає новий масив |
map() | Повільніший, створює новий масив | |
filter(), reduce() | Ще повільніше | Алокації, дод. операції |
8. Як прискорити методи масивів
| Метод | Оптимізація |
|---|---|
.map() | Використовувати всередині чисту стрілочну функцію без зовнішніх замикань |
.filter() | Не використовувати ланцюжки .map().filter().reduce(), а об'єднувати в один цикл |
.reduce() | Для складних операцій - винести накопичення в for |
.forEach() | Заміняти на for або for...of у "гарячому" коді |
.concat() / spread ([...a, ...b]) | Замінити на push.apply() або цикл при великих масивах |
9. Коротке резюме
| Причина | Чому повільніше |
|---|---|
| Callback-функції | створюють дод. виклики й контекст |
| Новий масив | виділяється нова пам'ять |
| Валідації та ітерація | вбудовані перевірки й протоколи |
| GC-навантаження | створюються тимчасові об'єкти |
| Цикли примітивніші | легше оптимізувати JIT-компілятору |
Висновок:
- Для задач, критичних за продуктивністю ->
for.- Для зрозумілого, декларативного коду ->
map,filter,reduce.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.