Копирование массива в JavaScript

Копирование массива нужно, когда мы хотим получить новый массив с такими же элементами, но не менять исходный.

let numbers = [1, 2, 3];
let copy = [...numbers];

copy.push(4);

console.log(numbers); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]

Что значит скопировать массив

Скопировать массив - значит создать новый массив и поместить в него элементы старого массива.

let fruits = ['apple', 'orange', 'banana'];
let fruitsCopy = [...fruits];

console.log(fruitsCopy); // ['apple', 'orange', 'banana']

После этого можно менять копию, не затрагивая исходный массив.

Присваивание не копирует массив

Если просто записать массив в другую переменную, копия не создается.

let fruits = ['apple', 'orange'];
let copy = fruits;

copy.push('banana');

console.log(copy); // ['apple', 'orange', 'banana']
console.log(fruits); // ['apple', 'orange', 'banana']

В переменной copy лежит ссылка на тот же массив. Поэтому изменение через copy видно и в fruits.

Это поведение связано с тем, что массивы в JavaScript относятся к ссылочным типам данных.

Копирование через spread

Самый частый способ скопировать массив - использовать spread-оператор .

let fruits = ['apple', 'orange'];
let copy = [...fruits];

console.log(copy); // ['apple', 'orange']

Квадратные скобки создают новый массив, а …fruits раскладывает в него элементы старого массива.

Такой способ хорошо читается и часто используется в современном коде.

Копирование через slice()

Метод slice() без аргументов тоже создает копию массива.

let fruits = ['apple', 'orange'];
let copy = fruits.slice();

console.log(copy); // ['apple', 'orange']

slice() не меняет исходный массив. Он возвращает новый массив.

let fruits = ['apple', 'orange'];
let copy = fruits.slice();

copy.push('banana');

console.log(fruits); // ['apple', 'orange']
console.log(copy); // ['apple', 'orange', 'banana']

Копирование через Array.from()

Array.from() создает новый массив из переданного значения.

let fruits = ['apple', 'orange'];
let copy = Array.from(fruits);

console.log(copy); // ['apple', 'orange']

Для обычного копирования массива этот способ встречается реже, чем spread или slice(). Но он полезен, когда нужно получить массив из перебираемого значения.

let letters = Array.from('HTML');

console.log(letters); // ['H', 'T', 'M', 'L']

Копия и изменение массива

Копирование удобно, когда нужно получить измененный вариант массива и оставить исходный без изменений.

let numbers = [1, 2, 3];

let updatedNumbers = [...numbers, 4];

console.log(numbers); // [1, 2, 3]
console.log(updatedNumbers); // [1, 2, 3, 4]

Такой подход часто используют в интерфейсах: старые данные остаются как были, а для обновления создается новый массив.

Поверхностная копия

Spread, slice() и Array.from() создают поверхностную копию массива.

Если внутри массива лежат простые значения, например строки или числа, обычно этого достаточно.

let numbers = [1, 2, 3];
let copy = [...numbers];

copy[0] = 100;

console.log(numbers); // [1, 2, 3]
console.log(copy); // [100, 2, 3]

Но если внутри массива лежат объекты, копируются ссылки на эти объекты.

let users = [
  { name: 'Анна' },
  { name: 'Олег' },
];

let copy = [...users];

copy[0].name = 'Мария';

console.log(users[0].name); // Мария

Сам массив стал новым, но объекты внутри остались теми же. Если нужно менять объект внутри копии, его тоже нужно копировать отдельно.

let users = [
  { name: 'Анна' },
  { name: 'Олег' },
];

let copy = users.map((user) => ({ ...user }));

copy[0].name = 'Мария';

console.log(users[0].name); // Анна
console.log(copy[0].name); // Мария

Что выбрать

Для обычного копирования массива чаще всего используют spread.

let copy = [...items];

slice() тоже подходит, особенно если в проекте уже используют такой стиль.

let copy = items.slice();

Array.from() удобен, когда нужно создать массив из строки, коллекции DOM-элементов или другого перебираемого значения.

Итого

Чтобы получить независимый массив, нужно создать новую копию, а не просто присвоить массив другой переменной.

  • let copy = array не копирует массив, а записывает ссылку.
  • […array] - частый способ скопировать массив.
  • array.slice() возвращает новый массив.
  • Array.from(array) тоже создает новый массив.
  • Эти способы создают поверхностную копию.
  • Если внутри массива объекты, их при необходимости копируют отдельно.