Skip to main content

Поверхнева копія масиву

Поверхнева копія (shallow copy) масиву, це новий масив з власним посиланням, у який скопійовано елементи верхнього рівня. Примітиви потрапляють у копію за значенням, а вкладені об'єкти та масиви, за посиланням, тому вони залишаються спільними з оригіналом.

Теорія

TL;DR

  • Поверхнева копія створює новий масив, тому copy === arr дає false, а зміни в копії не чіпають оригінал на верхньому рівні.
  • Найкоротший спосіб, spread: const copy = [...arr].
  • Те саме роблять arr.slice(), Array.from(arr) і [].concat(arr).
  • Усі вони копіюють лише верхній рівень: вкладені об'єкти й масиви лишаються спільними.
  • Заміна цілого елемента копії безпечна, зміна поля всередині вкладеного об'єкта, ні.
  • Для глибокої копії потрібен structuredClone(arr) або JSON.parse(JSON.stringify(arr)).

Швидкий приклад

javascript
const arr = [1, 2, 3]; const copy = [...arr]; console.log(copy); // [1, 2, 3] console.log(copy === arr); // false (це новий масив)

Spread оператор

Оператор розпакування ... розкладає елементи масиву в новий літерал. Це найсучасніший і найкоротший запис, і саме його очікують почути на співбесіді.

javascript
const arr = [1, 2, 3]; const copy = [...arr]; console.log(copy); // [1, 2, 3] console.log(copy === arr); // false (новий масив)

Копія при цьому поверхнева: якщо елементами були об'єкти, вони й далі посилаються на ті самі дані.

javascript
const arr = [{ name: 'Tim' }]; const copy = [...arr]; copy[0].name = 'Alex'; console.log(arr[0].name); // "Alex", бо об'єкт спільний

slice(), Array.from() і concat()

slice() без аргументів повертає новий масив, копію всього вихідного. Спосіб старий, але цілком надійний:

javascript
const arr = [1, 2, 3]; const copy = arr.slice(); console.log(copy); // [1, 2, 3]

Array.from() створює новий масив з ітерованого або масивоподібного значення:

javascript
const arr = [1, 2, 3]; const copy = Array.from(arr); console.log(copy); // [1, 2, 3]

Він корисний, коли потрібна не просто копія, а копія з перетворенням кожного елемента:

javascript
const doubled = Array.from(arr, x => x * 2); // [2, 4, 6]

concat() працює тому, що повертає новий масив, а не змінює вихідний:

javascript
const arr = [1, 2, 3]; const copy = [].concat(arr); console.log(copy); // [1, 2, 3]

Що залишається спільним

Усі перелічені методи копіюють тільки верхній рівень. Якщо в масиві лежать об'єкти або вкладені масиви, вони залишаються за посиланням.

javascript
const arr = [[1, 2], [3, 4]]; const copy = [...arr]; copy[0][0] = 99; console.log(arr[0][0]); // 99, бо вкладений масив спільний

Натомість заміна цілого елемента копії оригінал не зачіпає, бо змінюється слот нового масиву, а не спільний об'єкт:

javascript
copy[0] = [7, 8]; console.log(arr[0]); // [99, 2], оригінал не змінився

Порівняння способів

СпосібКодСтворює новий масивГлибока копія
Spread[...arr]ТакНі
slice()arr.slice()ТакНі
Array.from()Array.from(arr)ТакНі
concat()[].concat(arr)ТакНі

Підсумок простий: щоб зробити поверхневу копію масиву, пишіть const copy = [...arr];.

Глибоке копіювання

Коли потрібно скопіювати ще й вкладені структури, поверхневої копії замало:

javascript
const deep = structuredClone(arr);

structuredClone() вбудований у сучасні браузери і Node.js, коректно обробляє Map, Set, Date, типізовані масиви та циклічні посилання, але не вміє копіювати функції. Старіший прийом JSON.parse(JSON.stringify(arr)) простіший, проте втрачає undefined і функції, перетворює Date на рядок і кидає помилку на циклічних посиланнях.

Типові помилки

  • Вважати, що [...arr] дає повністю незалежний масив: вкладені об'єкти лишаються спільними.
  • Плутати копіювання з присвоєнням: const copy = arr не створює масив, це те саме посилання.
  • Брати JSON.parse(JSON.stringify(arr)) для масиву з Date чи функціями і дивуватися втраченим даним.
  • Думати, що slice() змінює масив: він повертає новий, на відміну від splice(), який змінює вихідний.
  • Копіювати масив ручним циклом з push() там, де достатньо одного виразу зі spread.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.