Пошук уроків, статей та іншого контенту
Як копіювати й об'єднувати об'єкти та масиви оператором spread та збирати залишок через rest.
Присвоєння const copy = original не копіює масив чи об'єкт — обидві змінні посилаються на ту саму структуру в пам'яті (це ми вже бачили в уроці про типи даних). Оператор spread (...) розгортає вміст масиву чи об'єкта в новий, справді окремий, і дозволяє легко об'єднувати кілька джерел в одне.
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3] — не змінився
console.log(copy); // [1, 2, 3, 4]const fruits = ["яблуко", "банан"];
const vegetables = ["морква", "буряк"];
const all = [...fruits, ...vegetables];
// ["яблуко", "банан", "морква", "буряк"]
const withExtra = [...fruits, "вишня", ...vegetables];
// порядок елементів у результаті — саме такий, як написаноТой самий принцип для об'єктів, доданий у стандарт трохи пізніше за масиви:
const defaults = { theme: "light", fontSize: 14 };
const userPreferences = { fontSize: 18 };
const settings = { ...defaults, ...userPreferences };
// { theme: "light", fontSize: 18 } — userPreferences перекриває однакові ключі defaultsПорядок має значення: властивості з об'єктів, розгорнутих пізніше, перекривають однакові ключі з тих, що розгорнуті раніше. Саме тому userPreferences написано другим — щоб користувацькі налаштування мали пріоритет над значеннями за замовчуванням.
Це найважливіший нюанс: spread створює новий зовнішній масив чи об'єкт, але вкладені об'єкти всередині залишаються спільними посиланнями:
const original = { name: "Олена", address: { city: "Київ" } };
const copy = { ...original };
copy.address.city = "Львів";
console.log(original.address.city); // "Львів" — теж змінилось! address — спільний об'єктДля глибокого копіювання вкладених структур потрібен окремий інструмент — наприклад, structuredClone(original) (сучасний вбудований спосіб) або рекурсивна функція копіювання. Це поширене джерело важко відстежуваних багів.
Ми вже бачили rest-параметри у функціях (модуль «Функції») — той самий символ ... у деструктуризації збирає всі властивості чи елементи, що лишились, у новий об'єкт або масив:
const user = { id: 1, name: "Максим", role: "admin", active: true };
const { id, ...rest } = user;
id; // 1
rest; // { name: "Максим", role: "admin", active: true } — усе, крім idconst numbers = [1, 2, 3, 4, 5];
const [first, ...others] = numbers;
first; // 1
others; // [2, 3, 4, 5]spread і rest використовують однаковий синтаксис ..., але роблять протилежне: spread розгортає значення (використовується там, де очікується список — у літералі масиву/об'єкта чи виклику функції), rest збирає значення (використовується в шаблоні деструктуризації чи параметрах функції).
function updateUserName(user, newName) {
return { ...user, name: newName }; // новий об'єкт, оригінал не зачеплено
}
const user = { id: 1, name: "Ірина", age: 25 };
const updated = updateUserName(user, "Марія");
user.name; // "Ірина" — оригінал не змінився
updated.name; // "Марія"Очікування, що spread робить глибоке копіювання — насправді копія поверхнева, вкладені об'єкти залишаються спільними.
Неправильний порядок розгортання: { ...userPreferences, ...defaults } перекриє налаштування користувача значеннями за замовчуванням — протилежне до того, що зазвичай потрібно.
Плутанина spread і rest, коли символ той самий — тип операції визначається контекстом (список значень vs шаблон деструктуризації).
Spread копіює й об'єднує масиви та об'єкти без мутації оригіналу — але лише поверхнево. Rest робить протилежне: збирає залишок значень у новий об'єкт чи масив під час деструктуризації. Разом вони — основний інструмент для роботи з даними в незмінному (immutable) стилі, до якого повернемось наприкінці цього модуля.
Спробуйте самостійно
Напишіть функцію removeField(obj, key), яка повертає новий об'єкт без вказаного поля, не змінюючи оригінальний obj. Підказка: деструктуризація з rest — саме для цього.