Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Методи масиву vs цикли». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Методи масивів (`map`, `filter`, `forEach`, `reduce` тощо) - це **абстракції над циклами**, написані всередині рушія JavaScript. **Ключове:** вони зручніші, коротші й зрозуміліші, але мають додаткові накладні витрати (виклик колбека на кожній ітерації, створення нового масиву, перевірки), через що можуть бути повільнішими за прості цикли (`for`, `for...of`, `while`) - особливо на великих масивах.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 1. Коротко: у чому суть Методи масивів (`map`, `filter`, `forEach`, `reduce` і т.д.) - це **абстракції над циклами**, написані всередині рушія JavaScript. Вони зручніші, коротші й зрозуміліші, але **мають додаткові накладні витрати**, через що можуть бути **повільнішими за прості цикли (**`for`**,** `for…of`**,** `while`**)** - особливо на великих масивах. --- ## 2. Що відбувається "під капотом" Коли ти пишеш: ```javascript arr.map(x => x * 2); ``` рушій робить приблизно таке: 1. Перевіряє, що `arr` дійсно масив. 2. Створює **новий масив** для результатів. 3. На кожній ітерації: - викликає **callback-функцію**; - передає їй три аргументи `(element, index, array)`; - зберігає результат у новий масив. 4. Повертає підсумковий масив. Це купа кроків, які **додають накладні витрати**, порівняно зі звичайним `for`-циклом, де ти просто виконуєш інструкції без обгорток і перевірок. --- ## 3. Чому саме повільніше | Причина | Що відбувається | |---|---| | **Callback-функція** | На кожній ітерації викликається нова функція -> зайві виклики стека | | **Створення нового масиву** | `map`, `filter`, `slice`, `concat` створюють копії (дод. пам'ять) | | **Перевірки й контекст** | Метод проходить валідації (довжина, holes, прототип, тип) | | **Неоптимізовані замикання** | Якщо колбек захоплює зовнішні змінні - ще більше накладних витрат | | **Функціональні принципи** | Ці методи "чисті" і не мутують дані -> потрібно більше алокацій | | **Цикли простіше оптимізувати JIT-компілятору** | Рушій (V8, SpiderMonkey) швидше оптимізує "голий" `for` | --- ## 4. Приклад порівняння ```javascript 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'); ``` На більшості рушіїв результат буде приблизно такий: ```javascript 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, бо це декларативно й зрозуміло: ```javascript {items.map(item => <Card key={item.id} {...item} />)} ``` Але якщо у тебе масив на **мільйони елементів** або цикл у "гарячому місці" (рендеринг, обробка бінарних даних, canvas, парсер) - краще використовувати: ```javascript 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`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.