Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Копіювання через Object.assign». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`Object.assign()` робить поверхневу копію (shallow copy): копіюється лише перший рівень властивостей, тому вкладені об'єкти та масиви переходять у копію за посиланням, а не за значенням.** Примітиви (`string`, `number`, `boolean`) копіюються за значенням і далі живуть незалежно, але будь-яка зміна всередині вкладеного об'єкта видно і в оригіналі, і в копії, бо це фізично один і той самий об'єкт у пам'яті. ```javascript const user = { name: 'Alice', address: { city: 'Kyiv' } }; const clone = Object.assign({}, user); clone.address.city = 'Lviv'; console.log(user.address.city); // 'Lviv', оригінал теж змінився ``` **Ключове:** `Object.assign()` копіює лише верхній рівень; для незалежної копії вкладених структур потрібен `structuredClone()` або інший deep clone.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`Object.assign()` створює поверхневу копію (shallow copy): він переносить у ціль лише власні перелічувані властивості першого рівня.** Це означає, що вкладені об'єкти та масиви копіюються за посиланням, тож оригінал і копія продовжують ділити одні й ті самі внутрішні структури. ## Теорія ### TL;DR - `Object.assign()` дає **shallow copy**, а не глибоку копію. - Копіюється **тільки перший рівень** властивостей. - Примітиви (`string`, `number`, `boolean`) копіюються **за значенням** і стають незалежними. - Вкладені об'єкти, масиви та функції копіюються **за посиланням**. - Зміна поля всередині вкладеного об'єкта видно і в оригіналі, і в копії. - Для незалежної копії використовуйте `structuredClone()`, `JSON.parse(JSON.stringify())` або `cloneDeep()` з Lodash. ### Швидкий приклад ```javascript const user = { name: 'Alice', address: { city: 'Kyiv', zip: 12345 } }; const clone = Object.assign({}, user); clone.address.city = 'Lviv'; console.log(user.address.city); // 'Lviv', а не 'Kyiv' ``` Чому так: - `Object.assign()` копіює лише **перший рівень властивостей**; - властивість `address` - це **об'єкт**, тому копіюється **посилання** на нього; - зміна вкладеного об'єкта (`city`) відображається в оригіналі. ### Як це виглядає в пам'яті ```javascript user.address ────┐ │ (один і той самий об'єкт) clone.address ────┘ ``` Обидва об'єкти, `user` і `clone`, посилаються на **один і той самий** `address`. Тому зміна всередині `address` зачіпає обидва. У самих об'єктах `user` і `clone` лежить не структура адреси, а адреса цієї структури в пам'яті, і `Object.assign()` чесно скопіював саме її. ### Але примітивні властивості копіюються нормально ```javascript const user = { name: 'Alice', age: 25 }; const clone = Object.assign({}, user); clone.name = 'Oleh'; console.log(user.name); // 'Alice', незалежно ``` Примітив не має внутрішньої структури, яку можна було б розділити, тому в копію лягає саме значення. Спільними лишаються **тільки вкладені об'єкти або масиви** (а також функції, бо функція теж об'єкт). ### Як уникнути цієї проблеми Якщо потрібна **глибока копія**, використовуйте один із варіантів. `structuredClone()`, вбудований сучасний спосіб: ```javascript const clone = structuredClone(user); clone.address.city = 'Lviv'; console.log(user.address.city); // 'Kyiv', оригінал не зачеплено ``` `JSON.parse(JSON.stringify())`, універсально, але з обмеженнями (втрачаються `undefined`, функції, `Date` перетворюється на рядок, циклічні посилання кидають помилку): ```javascript const clone = JSON.parse(JSON.stringify(user)); ``` `_.cloneDeep()` з Lodash, надійно і сумісно з усіма типами: ```javascript import _ from 'lodash'; const clone = _.cloneDeep(user); ``` ### Підсумок: що саме копіює Object.assign | Що копіює `Object.assign()` | Поведінка | | --- | --- | | Примітивні значення (`string`, `number`, `boolean`) | копіюються за значенням | | Вкладені об'єкти та масиви | копіюються **за посиланням** | | Функції | копіюються за посиланням | | Глибокі структури | не копіюються незалежно | ### Типові помилки - **Вважати `Object.assign({}, obj)` повним клоном.** Це лише верхній рівень; будь-яка вкладеність лишається спільною. - **Думати, що spread робить щось інше.** `{ ...obj }` має рівно ту саму поверхневу семантику, що й `Object.assign({}, obj)`. - **Мутувати вкладений об'єкт у стані додатка після «копіювання».** У React або Redux це класичне джерело багів: посилання не змінилося, тож компонент не перерендериться, зате оригінал уже зіпсовано. - **Сліпо застосовувати `JSON.parse(JSON.stringify())`.** Цей трюк мовчки викидає `undefined` і функції, перетворює `Date` на рядок і падає на циклічних посиланнях. - **Забувати, що `Object.assign()` мутує ціль.** Перший аргумент змінюється на місці, тому `Object.assign(user, patch)` це не копіювання, а оновлення `user`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.