Пошук уроків, статей та іншого контенту
Копіюватимете та об’єднуватимете об’єкти за допомогою spread-синтаксису й Object.assign.
Об’єкти в JavaScript зберігаються та передаються за посиланням. Якщо присвоїти один об’єкт іншій змінній, копії не створюється:
const user = {
name: "Олена",
age: 28
};
const anotherUser = user;
anotherUser.age = 29;
console.log(user.age); // 29Змінна anotherUser посилається на той самий об’єкт, що й user.
Поверхневе копіювання створює новий об’єкт, але копіює лише значення його властивостей на першому рівні.
const user = {
name: "Олена",
age: 28
};
const copiedUser = { ...user };
copiedUser.age = 29;
console.log(user.age); // 28
console.log(copiedUser.age); // 29У цьому випадку user і copiedUser — різні об’єкти.
Однак вкладені об’єкти не копіюються рекурсивно:
const user = {
name: "Олена",
settings: {
theme: "dark"
}
};
const copiedUser = { ...user };
copiedUser.settings.theme = "light";
console.log(user.settings.theme); // "light"Властивість settings містить посилання на вкладений об’єкт. Під час поверхневого копіювання це посилання копіюється без створення нового вкладеного об’єкта.
Для копіювання властивостей об’єкта можна використати оператор розпилення — ...:
const product = {
title: "Ноутбук",
price: 35000,
inStock: true
};
const copiedProduct = { ...product };
console.log(copiedProduct);
// { title: "Ноутбук", price: 35000, inStock: true }
console.log(copiedProduct === product); // falseSpread копіює власні перелічувані властивості об’єкта.
Нове значення можна одразу додати або змінити:
const defaultOptions = {
theme: "light",
language: "uk",
notifications: true
};
const userOptions = {
...defaultOptions,
theme: "dark"
};
console.log(userOptions);
// { theme: "dark", language: "uk", notifications: true }Порядок властивостей має значення: якщо одна й та сама властивість трапляється кілька разів, перемагає останнє значення.
const first = {
color: "blue",
size: "medium"
};
const second = {
color: "red"
};
const result = {
...first,
...second
};
console.log(result);
// { color: "red", size: "medium" }Якщо змінити порядок, результат буде іншим:
const result = {
...second,
...first
};
console.log(result);
// { color: "blue", size: "medium" }Spread зручно використовувати для створення нового об’єкта з кількох джерел:
const baseUser = {
name: "Олена",
role: "user"
};
const contactInfo = {
email: "olena@example.com",
phone: "+380501234567"
};
const user = {
...baseUser,
...contactInfo
};
console.log(user);
// {
// name: "Олена",
// role: "user",
// email: "olena@example.com",
// phone: "+380501234567"
// }Якщо об’єкти мають однакові властивості, значення з останнього об’єкта перезаписує попереднє:
const serverConfig = {
host: "localhost",
port: 3000,
debug: false
};
const localConfig = {
port: 4000,
debug: true
};
const config = {
...serverConfig,
...localConfig
};
console.log(config);
// { host: "localhost", port: 4000, debug: true }Такий підхід часто застосовують для оновлення стану:
const state = {
isLoading: false,
data: null,
error: null
};
const nextState = {
...state,
isLoading: true
};
console.log(nextState);
// { isLoading: true, data: null, error: null }
console.log(state.isLoading); // falseПочатковий об’єкт не змінюється, а створюється новий.
Метод Object.assign() копіює власні перелічувані властивості з одного або кількох об’єктів у цільовий об’єкт:
const source = {
name: "Олена",
age: 28
};
const target = {};
Object.assign(target, source);
console.log(target);
// { name: "Олена", age: 28 }
console.log(target === source); // falseПерший аргумент — цільовий об’єкт, а наступні аргументи — джерела властивостей.
const defaults = {
color: "blue",
size: "medium"
};
const customOptions = {
size: "large",
rounded: true
};
const options = Object.assign({}, defaults, customOptions);
console.log(options);
// { color: "blue", size: "large", rounded: true }Порожній об’єкт {} як перший аргумент дає змогу створити новий об’єкт і не змінювати defaults.
Для звичайного поверхневого копіювання та об’єднання результат часто однаковий:
const first = {
name: "Олена"
};
const second = {
age: 28
};
const spreadResult = {
...first,
...second
};
const assignResult = Object.assign({}, first, second);
console.log(spreadResult);
console.log(assignResult);Основна практична різниця полягає в тому, що Object.assign() змінює свій перший аргумент:
const target = {
name: "Олена"
};
const source = {
age: 28
};
Object.assign(target, source);
console.log(target);
// { name: "Олена", age: 28 }Spread натомість створює новий об’єкт:
const target = {
name: "Олена"
};
const source = {
age: 28
};
const result = {
...target,
...source
};
console.log(target);
// { name: "Олена" }
console.log(result);
// { name: "Олена", age: 28 }Порівняння:
{ ...object } — короткий синтаксис створення поверхневої копії;
{ ...first, ...second } — об’єднання в новий об’єкт;
Object.assign({}, first, second) — об’єднання в новий об’єкт;
Object.assign(target, source) — копіювання в уже наявний об’єкт із його зміною.
У сучасному коді для створення нового об’єкта часто використовують spread, оскільки він компактний і добре показує намір не змінювати початкові дані.
Spread не копіює вкладені об’єкти автоматично. Проте можна вручну створити нову копію кожного рівня, який потрібно змінити:
const user = {
name: "Олена",
settings: {
theme: "dark",
language: "uk"
}
};
const updatedUser = {
...user,
settings: {
...user.settings,
theme: "light"
}
};
console.log(user.settings.theme); // "dark"
console.log(updatedUser.settings.theme); // "light"Якщо вкладена властивість також містить об’єкт, потрібно копіювати й наступний рівень:
const account = {
id: 1,
profile: {
name: "Олена",
address: {
city: "Львів",
country: "Україна"
}
}
};
const updatedAccount = {
...account,
profile: {
...account.profile,
address: {
...account.profile.address,
city: "Київ"
}
}
};
console.log(account.profile.address.city); // "Львів"
console.log(updatedAccount.profile.address.city); // "Київ"Цей підхід називають іммутабельним оновленням: замість зміни наявних об’єктів створюються нові.
Об’єднання за допомогою spread також є поверхневим:
const defaultConfig = {
server: {
host: "localhost",
port: 3000
},
debug: false
};
const customConfig = {
server: {
port: 4000
}
};
const config = {
...defaultConfig,
...customConfig
};
console.log(config);
// {
// server: { port: 4000 },
// debug: false
// }Властивість server із customConfig повністю замінила server із defaultConfig. Значення host не було об’єднано автоматично.
Для вкладеного об’єднання потрібно явно розпилити кожен рівень:
const config = {
...defaultConfig,
...customConfig,
server: {
...defaultConfig.server,
...customConfig.server
}
};
console.log(config);
// {
// server: { host: "localhost", port: 4000 },
// debug: false
// }Spread і Object.assign() копіюють власні перелічувані властивості об’єкта.
Властивості з прототипу не копіюються:
const parent = {
inheritedValue: 10
};
const object = Object.create(parent);
object.ownValue = 20;
const copy = {
...object
};
console.log(copy.ownValue); // 20
console.log(copy.inheritedValue); // undefinedТакож ці операції не створюють повну копію дескрипторів властивостей, прототипу чи спеціальної поведінки об’єкта. Вони копіюють значення властивостей у новий звичайний об’єкт.
Нижче функція створює оновлений профіль користувача. Початковий об’єкт не змінюється, а вкладені налаштування копіюються окремо:
function updateUserProfile(user, changes) {
return {
...user,
...changes,
settings: {
...user.settings,
...(changes.settings || {})
}
};
}
const user = {
id: 1,
name: "Олена",
email: "olena@example.com",
settings: {
theme: "dark",
language: "uk",
notifications: true
}
};
const updatedUser = updateUserProfile(user, {
name: "Олена Петренко",
settings: {
theme: "light"
}
});
console.log(updatedUser);
// {
// id: 1,
// name: "Олена Петренко",
// email: "olena@example.com",
// settings: {
// theme: "light",
// language: "uk",
// notifications: true
// }
// }
console.log(user.name); // "Олена"
console.log(user.settings.theme); // "dark"У функції:
...user копіює властивості користувача.
...changes додає зміни та перезаписує властивості з такими самими іменами.
Вкладений settings копіюється окремо.
...(changes.settings || {}) запобігає помилці, якщо налаштування не передали.
Object.assign() без порожнього цільового об’єктаconst user = {
name: "Олена"
};
const changes = {
age: 28
};
Object.assign(user, changes);
console.log(user);
// { name: "Олена", age: 28 }Цей код змінює user. Якщо потрібна нова копія, використовуйте:
const updatedUser = Object.assign({}, user, changes);або:
const updatedUser = {
...user,
...changes
};const original = {
preferences: {
color: "blue"
}
};
const copy = { ...original };
copy.preferences.color = "red";
console.log(original.preferences.color); // "red"Spread скопіював посилання на preferences, а не створив новий вкладений об’єкт.
Якщо потрібно змінити вкладену властивість без зміни оригіналу:
const copy = {
...original,
preferences: {
...original.preferences,
color: "red"
}
};const defaults = {
timeout: 3000
};
const userOptions = {
timeout: 5000
};
const options = {
...userOptions,
...defaults
};
console.log(options.timeout); // 3000Тут значення за замовчуванням перезаписало значення користувача. Зазвичай об’єкт із налаштуваннями користувача потрібно розміщувати після об’єкта за замовчуванням:
const options = {
...defaults,
...userOptions
};
console.log(options.timeout); // 5000Поверхнева копія не робить об’єкт незмінним і не захищає його від змін:
const original = {
nested: {
value: 1
}
};
const copy = { ...original };
copy.nested.value = 2;
console.log(original.nested.value); // 2Копіювання та заборона змін — різні завдання. Для складних структур потрібно продумати глибоке копіювання або використовувати інші засоби залежно від типу даних.
Присвоєння об’єкта копіює посилання, а не сам об’єкт.
Spread { ...object } створює поверхневу копію.
Object.assign({}, object) також створює поверхневу копію.
Object.assign(target, source) змінює target.
Під час об’єднання властивість із пізнішого об’єкта перезаписує властивість із попереднього.
Вкладені об’єкти та масиви не копіюються рекурсивно.
Для оновлення вкладених даних потрібно копіювати кожен рівень, який змінюється.
Spread і Object.assign() копіюють власні перелічувані властивості, але не прототип і не повну структуру об’єкта.