Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Оператори розповсюдження та залишку в JavaScript: відмінності та приклади». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Spread (`...`)** розгортає ітерабельне значення в окремі елементи; rest (`...`) збирає залишкові елементи в масив. Однаковий синтаксис, протилежні завдання. ```javascript // Spread: розпакувати const merged = [...[1, 2], 3]; // [1, 2, 3] // Rest: зібрати function sum(first, ...rest) { return first + rest.reduce((a, b) => a + b, 0); } sum(1, 2, 3); // 6 ``` **Ключове:** позиція визначає роль. Праворуч або у виклику = spread. Ліворуч у параметрах або деструктуризації = rest.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Spread (`...`) та rest (`...`)** мають однаковий синтаксис, але роблять протилежні речі. Spread розпаковує ітерабельне значення в окремі елементи; rest збирає окремі елементи в один масив. ## Теорія ### TL;DR - Spread - це як висипати вміст сумки на стіл: все виходить окремо - Rest - це як скласти залишки назад у сумку: все збирається разом - Головна різниця: spread стоїть праворуч (або всередині виклику функції), rest стоїть ліворуч (у параметрах або деструктуризації) - Копіюєш, об'єднуєш або передаєш дані? Використовуй spread. Захоплюєш зайві аргументи або залишкові пропси? Використовуй rest - Rest завжди повертає масив, навіть якщо зайвих елементів немає (повертає `[]`) ### Швидкий приклад ```javascript // Spread: розпаковує масив в окремі елементи const nums = [1, 2, 3]; const more = [...nums, 4, 5]; // [1, 2, 3, 4, 5] // Spread у виклику функції function add(a, b, c) { return a + b + c; } add(...nums); // 6 // Rest: збирає залишкові аргументи в масив function sum(first, ...rest) { return first + rest.reduce((a, b) => a + b, 0); } sum(1, 2, 3, 4); // 10, rest = [2, 3, 4] ``` Контекст визначає, який оператор ти отримуєш. Ті самі три крапки, різні ролі. ### Головна відмінність Spread з'являється праворуч від операторів присвоєння або всередині викликів функцій. Він розбиває ітерабельне значення на частини і повертає окремі елементи. Rest з'являється ліворуч у параметрах функцій або у шаблонах деструктуризації (destructuring). Він збирає все, що залишилось, і загортає в масив. Один розширює, інший стискає. ### Коли використовувати - Скопіювати масив без мутації: `[...arr]` - Об'єднати об'єкти: `{ ...obj1, ...obj2 }` - Передати масив як окремі аргументи у функцію: `func(...arr)` - Захопити змінну кількість аргументів функції: `function fn(a, ...rest) {}` - Деструктурувати з залишковими пропсами: `const { x, ...rest } = obj` ### Таблиця порівняння | Характеристика | Spread (`...`) | Rest (`...`) | |---|---|---| | Позиція | Праворуч (присвоєння, виклики) | Ліворуч (параметри, деструктуризація) | | Ефект | Розгортає в окремі елементи | Збирає залишки в масив | | У функціях | `func(...arr)` передає окремі аргументи | `func(a, ...rest)` збирає зайві аргументи | | Масиви | `[...arr1, ...arr2]` об'єднує | `const [first, ...rest] = arr` розбиває | | Об'єкти (ES2018+) | `{ ...obj1, ...obj2 }` об'єднує | `const { a, ...rest } = obj` витягує решту | | Коли використовувати | Копіювання, об'єднання, передача | Змінна кількість аргументів, залишкові пропси | ### Як це працює JavaScript-рушії обробляють `...` як синтаксичний цукор: spread ітерує джерело через `Symbol.iterator` (для масивів) або `Object.keys` (для об'єктів), повертаючи значення по одному. Rest сканує список параметрів під час виклику і пакує хвостові аргументи в новий масив. Обидва створюють поверхневі копії, тому вкладені об'єкти спільно використовують посилання з оригіналом. У React-проєктах spread зустрічається постійно. Шаблон `{ ...state, updatedField: newValue }` - стандартний спосіб зберігати незмінність у редюсерах без зайвих бібліотек. ### Типові помилки **Передача масиву у rest-функцію без spread:** ```javascript function add(...nums) { return nums.reduce((a, b) => a + b); } add([1, 2, 3]); // [1,2,3] стає першим аргументом, результат NaN add(...[1, 2, 3]); // правильно: 6 ``` **Припущення, що spread робить глибоку копію:** ```javascript const a = { nested: { x: 1 } }; const b = { ...a }; a.nested.x = 99; console.log(b.nested.x); // 99 - вкладений об'єкт спільний! // Виправлення: розповсюджуй вкладений об'єкт теж const c = { ...a, nested: { ...a.nested } }; ``` **Розповсюдження не-ітерабельного значення:** ```javascript [...42]; // TypeError: 42 is not iterable [...'abc']; // працює: ['a', 'b', 'c'] - рядки є ітерабельними [...new Set([1, 2, 2])]; // працює: [1, 2] - Set є ітерабельним ``` **Rest має бути останнім параметром:** ```javascript function fn(a, ...rest, b) {} // SyntaxError function fn(a, ...rest) {} // правильно ``` ### Реальне використання - React: `<Button {...buttonProps} />` передає пропси в JSX - Redux редюсери: `{ ...state, count: state.count + 1 }` для незмінних оновлень - Express middleware: `{ ...req.query, filter: 'active' }` додає параметри запиту - Видалення дублікатів через Set: `[...new Set([1, 2, 2, 3])]` повертає `[1, 2, 3]` ### Питання для практики **Q:** Що виведе `const a = [1]; const b = [...a]; a.push(2); console.log(b);`? **A:** `[1]`. Spread створює новий масив, тому `b` не залежить від `a`. **Q:** Що використовували до object spread (ES2018)? **A:** `Object.assign({}, obj1, obj2)`. Babel досі транспілює object spread до `Object.assign` для старих цільових платформ, тому результат однаковий. **Q:** Що повертає rest, якщо зайвих аргументів немає? **A:** Порожній масив `[]`. Це зручніше за старий об'єкт `arguments`, який був схожий на масив, але масивом не був. **Q:** Чи можна розповсюдити `Set` або `Map`? **A:** Так, обидва є ітерабельними. `[...new Set([1, 2, 2])]` дає `[1, 2]`. Зручно для швидкого видалення дублікатів. **Q:** Чому rest-параметри кращі за об'єкт `arguments`? **A:** `arguments` схожий на масив, але не є ним, тому `.map()` і `.reduce()` не працюють з ним напряму. Rest повертає справжній масив. До того ж стрілкові функції взагалі не мають власного `arguments`. ## Приклади ### Об'єднання налаштувань користувача ```javascript const defaults = { theme: 'light', fontSize: 14, showSidebar: true }; const userPrefs = { fontSize: 18, language: 'en' }; // Пізніші ключі перекривають попередні const settings = { ...defaults, ...userPrefs }; // { theme: 'light', fontSize: 18, showSidebar: true, language: 'en' } ``` Порядок має значення. `...userPrefs` стоїть другим, тому його `fontSize` перезаписує значення за замовчуванням. Цей шаблон стандартний для об'єднання конфігів та тем. ### Логер зі змінною кількістю аргументів ```javascript function log(level, ...messages) { const prefix = `[${level.toUpperCase()}]`; console.log(prefix, ...messages); // знову розповсюджуємо messages } log('info', 'User logged in', 'userId:', 42); // [INFO] User logged in userId: 42 ``` Rest збирає `messages` у масив. Потім spread передає їх назад як окремі аргументи в `console.log`. Rest збирає, spread розповсюджує.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.