Оригінальна публікація: How to clone an array in JavaScript
В JavaScript є багато способів зробити що завгодно. Зараз ми будемо працювати з масивами.
1. Оператор Spread (поверхнева копія)
З того часу, як з’явився ES6 — це найпопулярніший метод. Це короткий синтаксис, який є неймовірно корисним під час використання таких бібліотек, як React і Redux.
numbers = [1, 2, 3];
numbersCopy = [...numbers];
Примітка: ненадійно копіює багатовимірні масиви. Значення масиву/об’єкта копіюються за посиланням, а не за значенням.
Це добре:
numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] та [1, 2, 3, 4]
// числа залишаються без змін
Це не добре:
nestedNumbers = [[1], [2]];
numbersCopy = [...nestedNumbers];
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Обоє були змінені, оскільки мають спільні посилання
2. Старий добрий цикл for() (поверхнева копія)
Я думаю, що цей підхід найменш популярний, враховуючи, наскільки трендовим стало функціональне програмування в наших колах.
Чистий чи нечистий, декларативний чи імперативний, він виконує роботу!
numbers = [1, 2, 3];
numbersCopy = [];
for (i = 0; i < numbers.length; i++) {
numbersCopy[i] = numbers[i];
}
Примітка: ненадійно копіює багатовимірні масиви. Оскільки ви використовуєте оператор =
, він призначатиме об’єкти/масиви за посиланням, а не за значенням.
Це добре:
numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] та [1, 2, 3, 4]
// числа залишаються без змін
Це не добре:
nestedNumbers = [[1], [2]];
numbersCopy = [];
for (i = 0; i < nestedNumbers.length; i++) {
numbersCopy[i] = nestedNumbers[i];
}
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Обоє були змінені, оскільки мають спільні посилання
3. Старий добрий цикл while() (поверхнева копія)
Такий самий, що й for
: нечистий, імперативний, бла-бла-бла… Але працює!?
numbers = [1, 2, 3];
numbersCopy = [];
i = -1;
while (++i < numbers.length) {
numbersCopy[i] = numbers[i];
}
Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.
Це добре:
numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] та [1, 2, 3, 4]
// числа залишаються без змін
Це не добре:
nestedNumbers = [[1], [2]];
numbersCopy = [];
i = -1;
while (++i < nestedNumbers.length) {
numbersCopy[i] = nestedNumbers[i];
}
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Обоє були змінені, оскільки мають спільні посилання
4. Array.map (поверхнева копія)
На сучасній території ми знайдемо функцію map
. Закорінена в математиці map
— це концепція перетворення множини в множину іншого типу зі збереженням структури.
Простими словами: Array.map
повертає масив тої самої довжини кожного разу.
Щоб подвоїти список чисел, використайте map
із функцією double
.
numbers = [1, 2, 3];
double = (x) => x * 2;
numbers.map(double);
А як щодо клонування??
Правда, ця публікація про клонування масивів. Щоб дублювати масив, просто поверніть елемент у виклику map
.
numbers = [1, 2, 3];
numbersCopy = numbers.map((x) => x);
Якщо ви хочете побути математиками, (x) => x
називається тотожністю. Вона повертає будь-який параметр, який було надано.
map(identity)
клонує список.
identity = (x) => x;
numbers.map(identity);
// [1, 2, 3]
Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.
5. Array.filter (поверхнева копія)
Ця функція повертає масив, як і map
, але не гарантовано, що він буде такої ж довжини.
А якщо відфільтрувати парні числа?
[1, 2, 3].filter((x) => x % 2 === 0);
// [2]
Довжина вхідного масиву дорівнює 3, а вихідного — 1.
Проте, якщо предикат вашого filter
завжди повертає true
, ви отримаєте дублікат!
numbers = [1, 2, 3];
numbersCopy = numbers.filter(() => true);
Кожен елемент проходить тест, тому він повертається.
Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.
6. Array.reduce (поверхнева копія)
Мені ніяково використовувати reduce
для клонування масиву, тому що вона набагато потужніша за це. Але раз ви просите…
numbers = [1, 2, 3];
numbersCopy = numbers.reduce((newArray, element) => {
newArray.push(element);
return newArray;
}, []);
reduce
перетворює початкове значення, коли воно проходить список.
В прикладі початкове значення є порожнім масивом і ми заповнюємо його кожним елементом на ходу. Цей масив повинен бути повернений з функції, щоб його використати в наступній ітерації.
Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.
7. Array.slice (поверхнева копія)
slice
повертає поверхневу копію масиву на основі наданого вами початкового/кінцевого індексу.
Якщо ми хочемо перші 3 елементи:
[1, 2, 3, 4, 5].slice(0, 3);
// [1, 2, 3]
// Починається з індексу 0, закінчується на індексі 3
Якщо ми хочемо всі елементи, не вказуємо жодних параметрів:
numbers = [1, 2, 3, 4, 5];
numbersCopy = numbers.slice();
// [1, 2, 3, 4, 5]
Примітка: це поверхнева копія, тому вона також призначає об’єкти/масиви за посиланням, а не за значенням.
8. JSON.parse та JSON.stringify (глибока копія)
JSON.stringify
перетворює об’єкт на рядок.
JSON.parse
перетворює рядок на об’єкт.
Якщо їх поєднати, то можна перетворити об’єкт на рядок, а потім змінити процес навпаки, щоб створити нову структуру даних.
Примітка: надійно копіює глибоко вкладені об’єкти/масиви!
nestedNumbers = [[1], [2]];
numbersCopy = JSON.parse(JSON.stringify(nestedNumbers));
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1], [2]]
// [[1, 300], [2]]
// Два абсолютно окремі масиви!
9. Array.concat (поверхнева копія)
concat
поєднує масиви зі значеннями або іншими масивами.
[1, 2, 3].concat(4); // [1, 2, 3, 4]
[1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]
Якщо ви не надасте нічого або порожній масив, повернеться поверхнева копія.
[1, 2, 3].concat(); // [1, 2, 3]
[1, 2, 3].concat([]); // [1, 2, 3]
Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.
10. Array.from (поверхнева копія)
Може перетворити будь-який ітерований об’єкт в масив. Надання масиву повертає поверхневу копію.
numbers = [1, 2, 3];
numbersCopy = Array.from(numbers);
// [1, 2, 3]
Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.
Висновок
Ну, було весело?
Я спробував клонувати, використовуючи лише 1 крок. Ви знайдете ще багато інших способів, якщо використаєте декілька методів та технік.