Скопіюйте масив за значенням


1744

Під час копіювання масиву в JavaScript в інший масив:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

Я зрозумів, що arr2відноситься до того ж масиву arr1, що й до нового, незалежного масиву. Як я можу скопіювати масив, щоб отримати два незалежні масиви?


3
Схоже, що зараз у Chrome 53 та Firefox 48 ми маємо круті показники sliceта spliceоперації, а також новий оператор розповсюдження та Array.fromнабагато повільніше впроваджуємо. Подивіться на perfjs.fnfo
Пенкроф

jsben.ch/#/wQ9RU <= цей орієнтир дає огляд різних способів копіювання масиву
EscapeNetscape


Для цього масиву (той , який містить примітивні рядки) , ви можете використовувати var arr2 = arr1.splice();для глибокої копії, але цей метод не буде працювати , якщо елементи в масиві містять літерні структури (тобто []або {}) або об'єкти - прототипів (тобто function () {}, newі т.д.). Дивіться мою відповідь нижче для подальших рішень.
tfmontague

16
Настав 2017 рік, тому ви можете подумати про використання функцій ES6: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich,

Відповіді:


2700

Використовуй це:

var newArray = oldArray.slice();

В основному, slice()операція клонує масив і повертає посилання на новий масив.

Також зауважте, що:

Для довідок, рядків і чисел (а не власне об'єкта) slice()копіює посилання на об'єкт у новий масив. І вихідний, і новий масив стосуються одного і того ж об'єкта. Якщо посилання на об'єкт змінюється, зміни видно як новому, так і оригінальному масивам.

Примітиви, такі як рядки та числа, незмінні, тому зміни рядка чи числа неможливі.


9
Щодо продуктивності, наступні тести jsPerf фактично показують, що var arr2 = arr1.slice () так само швидко, як і var arr2 = arr1.concat (); JSPerf: jsperf.com/copy-array-slice-vs-concat/5 та jsperf.com/copy-simple-array . Результат jsperf.com/array-copy/5 начебто здивував мене до того, що мені цікаво, чи дійсний тестовий код.
Коен

94
Незважаючи на те, що це вже отримало багато грошей, це заслуговує на інше, оскільки воно належним чином описує посилання в JS, що, на жаль, є рідкісним.
Уейн

34
@ GáborImre ви б додали цілу бібліотеку просто для читання? Дійсно? Я б просто дописав коментар, якби мене хвилювали читабельність. Дивіться: var newArray = oldArray.slice (); // Clone oldArray to newArray
dudewad

12
@ GáborImre Я розумію, що. Але відповісти на конкретну інженерну проблему, включивши цілу бібліотеку, на мою думку, не корисно, це дизайн. Я бачу, що розробники роблять це багато, і тоді ви закінчуєте проект, який включав цілий фреймворк, щоб замінити необхідність написати одну функцію. Тільки мій МО, хоча.
dudewad

5
Урок: Чи не слід плутати .slice()з .splice(), що дає порожній масив. Велика різниця.
crazypeter

530

У Javascript методи глибокого копіювання залежать від елементів масиву. Почнемо з цього.

Три типи елементів

Елементами можуть бути: буквальні значення, буквальні структури або прототипи.

// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';

// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};

// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}`

З цих елементів ми можемо створити три типи масивів.

// 1) Array of literal-values (boolean, number, string) 
const type1 = [true, 1, "true"];

// 2) Array of literal-structures (array, object)
const type2 = [[], {}];

// 3) Array of prototype-objects (function)
const type3 = [function () {}, function () {}];

Методи глибокої копіювання залежать від трьох типів масиву

Виходячи з типів елементів у масиві, ми можемо використовувати різні методи для глибокої копіювання.

Методи глибокої копіювання Javascript за типами елементів

  • Масив буквених значень (type1) , , і методи можуть бути використані для глибоких копії масивів з літерними значеннями (булева, число і рядок) тільки; де оператор Spread має найкращі показники ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).
    [...myArray]myArray.splice(0)myArray.slice()myArray.concat()[...myArray]

  • Масив буквальних значень (type1) і буквальних-структури (TYPE2) методика може бути використана для глибокого копіювання буквених значень (булева, число, рядок) і літерні структури (масив, об'єкт), але не Prototype об'єктів.
    JSON.parse(JSON.stringify(myArray))

  • Усі масиви (type1, type2, type3) Техніка
    jQuery $.extend(myArray)може використовуватися для глибокого копіювання всіх типів масивів. Бібліотеки, як Underscore і Lo-dash, пропонують подібні функції глибокого копіювання, ніж jQuery $.extend() , але мають меншу продуктивність. Що більш дивно, $.extend()але більш висока продуктивність, ніж JSON.parse(JSON.stringify(myArray))техніка http://jsperf.com/js-deep-copy/15 .
    А для тих розробників, які ухиляються від сторонніх бібліотек (наприклад, jQuery), ви можете використовувати таку спеціальну функцію; який має більш високу продуктивність, ніж $ .extend, і копіює всі масиви.

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

Отже, щоб відповісти на питання ...

Питання

var arr1 = ['a','b','c'];
var arr2 = arr1;

Я зрозумів, що arr2 відноситься до того ж масиву, що і arr1, а не до нового, незалежного масиву. Як я можу скопіювати масив, щоб отримати два незалежні масиви?

Відповідь

Оскільки arr1це масив буквальних значень (булевих, числових або рядкових), ви можете використовувати будь-яку техніку глибокого копіювання, обговорену вище, де оператор розповсюдження ...має найвищу продуктивність.

// Highest performance for deep copying literal values
arr2 = [...arr1];

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
arr2 = arr1.slice();
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above

1
Багато з цих підходів не працюють добре. Використання оператора присвоєння означає, що вам потрібно перепризначити початкове буквальне значення arr1. Дуже рідко так трапляється. Використовуйте spliceоблітерати arr1, тому це зовсім не копія. Використання JSONне вдасться, якщо будь-яке значення в масиві є функціями або має прототипи (наприклад, a Date).
Dancrumb

Використання зрощення - часткове рішення. Він вийде з ладу в набагато більше випадках, ніж JSON. Сплайс створює глибоку копію рядків і чисел, коли він переміщує значення - ніколи не сказав, що повертає копію.
tfmontague

1
Чому сплайс (0)? Чи не повинен це бути slice ()? Я думаю, що це не повинно змінювати оригінальний масив, який робить зрощення. @JamesMontagne
helpse

2
сплайс створить покажчики на елементи в оригінальному масиві (неглибока копія). splice (0) виділить нову пам'ять (глибоку копію) для елементів масиву, які є числами або рядками, та створить покажчики для всіх інших типів елементів (дрібної копії). Передаючи початковому значенню нульове значення методу функції сплайсингу, він не зробить жодних елементів з вихідного масиву, і тому не змінює його.
tfmontague

1
Власне, є лише один тип масиву: масив "somethings". Немає різниці між [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]будь-яким іншим масивом.
wizzwizz4

189

...Для копіювання масивів можна використовувати розвороти масивів.

const itemsCopy = [...items];

Також якщо ви хочете створити новий масив з наявним його частиною:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

Зараз розширення масиву підтримуються у всіх основних браузерах, але якщо вам потрібна стара версія підтримки, використовуйте typecript або babel і компілюйте в ES5.

Докладніше про спреди


1
Для глибокої копії це не працюватиме. Deep Clone масив у JavaScript .
SNag

151

Не потрібен jQuery ... Робочий приклад

var arr2 = arr1.slice()

Це копіює масив із вихідної позиції 0через кінець масиву.

Важливо зазначити, що він буде працювати так, як очікувалося для примітивних типів (рядок, число тощо), а також пояснити очікувану поведінку для референтних типів ...

Якщо у вас є масив типів посилань, скажімо про тип Object. Масив буде скопійовано, але обидва масиви будуть містити посилання на ті самі Object. Отже, у цьому випадку здається, що масив копіюється за посиланням, хоча масив фактично скопійований.


12
Ні, це не було б глибокою копією.
jondavidjohn

Спробуйте це; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh

2
Чим відрізняється ця відповідь від прийнятої відповіді?
Ісаак Пак

помилка в консолі для даного прикладу "TypeError: window.addEvent не є функцією"
Раві Шарма

72

Альтернативою sliceє те concat, яке можна використовувати двома способами. Перший з них, мабуть, читабельніший, оскільки цільова поведінка дуже чітка:

var array2 = [].concat(array1);

Другий метод:

var array2 = array1.concat();

Коен (у коментарях) зазначив, що цей останній метод має кращі показники .

Це працює таким чином, що concatметод створює новий масив, що складається з елементів в об'єкті, на який він викликається, а з елементами будь-яких масивів, переданих йому в якості аргументів. Отже, коли ніяких аргументів не передано, він просто копіює масив.

Чи Penkman, а також в коментарях, вказує на те , що якщо є шанс , що array1це undefined, ви можете повернути порожній масив таким чином :

var array2 = [].concat(array1 || []);

Або для другого способу:

var array2 = (array1 || []).concat();

Зверніть увагу , що ви можете зробити це з допомогою slice: var array2 = (array1 || []).slice();.


31
Насправді ви також можете зробити: var array2 = array1.concat (); Це набагато швидше щодо продуктивності. (JSPerf: jsperf.com/copy-simple-array та jsperf.com/copy-array-slice-vs-concat/5
Cohen

5
Варто зазначити, що якщо array1 не є масивом, то [].concat(array1)повертається, [array1]наприклад, якщо його визначено, ви отримаєте [undefined]. Я інколи роблюvar array2 = [].concat(array1 || []);
lee penkman

60

Ось як я це зробив, випробувавши багато підходів:

var newArray = JSON.parse(JSON.stringify(orgArray));

Це створить нову глибоку копію, не пов’язану з першою (не дрібну копію).

Також це, очевидно, не буде клонувати події та функції, але добре, що ви можете це зробити в одному рядку, і його можна використовувати для будь-якого виду об’єктів (масиви, рядки, числа, об'єкти ...)


4
Це найкращий. Я давно використовую той самий метод і вважаю, що більше немає сенсу в рекурсивних петлях старої школи
Володимир Харлампіді

1
Пам’ятайте, що ця опція не справляється з графоподібними структурами: збої за наявності циклів і не зберігають спільних посилань.
Рубен

1
Це також не вдається для речей, як Date, і справді, всього, що має прототип. Крім того, undefineds перетворюються на nulls.
Dancrumb

7
Хіба ніхто не хоробрий, щоб прокоментувати велику неефективність як процесора, так і пам'яті, щоб серіалізувати текст і потім розібратися назад до об'єкта?
Лоуренс Дол

3
Це рішення - єдине, що працювало. Використання slice () - це дійсно підроблене рішення.

20

Деякі з згаданих методів добре працюють при роботі з простими типами даних, такими як число або рядок, але коли масив містить інші об'єкти, ці методи не вдається. Коли ми намагаємось передати будь-який об’єкт з одного масиву в інший, він передається як опорний, а не об'єкт.

Додайте наступний код у свій файл JavaScript:

Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (i in this) {
        if (i == 'clone') 
            continue;
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        } 
        else 
            newObj[i] = this[i]
    } return newObj;
};

І просто використовувати

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

Це спрацює.


2
я отримую цю помилку, коли я додаю цей код на свою сторінку "Uncaught RangeError: Максимальний розмір стека викликів перевищено"
побачив

1
Вибачте, ця помилка виникає в хромі, якщо arr1 не оголошено. тому я скопіював вищевказаний код, і я отримаю помилку, однак, якщо я оголошу масив arr1, то я не отримую помилку. Ви можете вдосконалити відповідь, оголосивши arr1 трохи вище arr2, я бачу, що там є досить багато «нас», які не визнали, що ми повинні оголосити arr1 (почасти тому, що коли я оцінював вашу відповідь, я поспішав і знадобилося щось, що "просто працює")
побачили

.slice()все ще працює добре, навіть якщо у вашому масиві є об’єкти: jsfiddle.net/edelman/k525g
Jason

7
@Jason, але об'єкти все ще вказують на той самий об'єкт, тож зміна одного змінить інший. jsfiddle.net/k525g/1
Самуель

Відмінний код. В мене є одне запитання, я фактично намагався скопіювати один масив в інший, як цей var arr1 = new Array (), а потім var arr2 = arr1; Якщо я щось міняю в arr2, зміна трапляється також у arr1. Однак якщо я використовую створений вами прототип-клон, він фактично створює цілком новий екземпляр цього масиву або іншими словами копіює його. Так це проблема в браузері? або javascript за замовчуванням встановлює дві змінні однією вказівкою на іншу за допомогою покажчиків, коли хтось робить var arr2 = arr1, і чому це не відбувається з цілими змінними? дивіться jsfiddle.net/themhz/HbhtA
themhz


17

Я особисто думаю, що Array.from - це більш читабельне рішення. До речі, просто остерігайтесь його підтримки в браузері.

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);

1
Так, це дуже читабельно. .slice()Рішення повністю неінтуітівнимі. Дякую за це
Банаго

15

Важливо!

Більшість відповідей тут працює для конкретних випадків .

Якщо вам не байдуже використання глибоких / вкладених об'єктів та реквізитів ( ES6 ):

let clonedArray = [...array]

але якщо ви хочете зробити глибокий клон, скористайтеся цим:

let cloneArray = JSON.parse(JSON.stringify(array))


Для користувачів квартир:

let clonedArray = _.clone(array) документація

і

let clonedArray = _.cloneDeep(array) документація


11

Якщо ви перебуваєте в середовищі ECMAScript 6 , за допомогою оператора Spread ви можете це зробити так:

var arr1 = ['a','b','c'];
var arr2 = [...arr1]; //copy arr1
arr2.push('d');

console.log(arr1)
console.log(arr2)
<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>


9

Додавання до рішення array.slice (); майте на увазі, що якщо у вас є багатовимірний масив, підмасиви будуть скопійовані посиланнями. Що ви можете зробити - це циклічно і розрізати () кожен підмасив окремо

var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();

arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);

function arrCpy(arrSrc, arrDis){
 for(elm in arrSrc){
  arrDis.push(arrSrc[elm].slice());
}
}

var arr3=[];
arrCpy(arr,arr3);

arr3[1][1] = 77;

console.log(arr3[1][1]);
console.log(arr[1][1]);

ті ж речі відносяться до масиву об'єктів, вони будуть скопійовані за посиланням, їх потрібно копіювати вручну


Ця відповідь заслуговує на місце у верхній частині сторінки! Я працював з багатовимірними підмасивами і не міг зрозуміти, чому внутрішні масиви завжди копіюються ref, а не val. Ця проста логіка вирішила мою проблему. Я дав би вам +100, якщо можливо!
Мак

8

Примітивні значення завжди передаються за його значенням (копіюються). Однак значення сполук передаються шляхом посилання.

Тож як ми скопіюємо цю заборгованість?

let arr = [1,2,3,4,5];

Скопіюйте масив в ES6

let arrCopy = [...arr]; 

Скопіюйте n масив у ES5

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

Чому `нехай arrCopy = arr` не проходить за значенням?

Передача однієї змінної до іншої на значеннях сполук, таких як Object / Array, поводяться по-різному. Використовуючи оператор призначення на значення copand, ми передаємо посилання на об'єкт. Ось чому значення обох масивів змінюються під час видалення / додавання елементів arr.

Винятки:

arrCopy[1] = 'adding new value this way will unreference';

Призначаючи нове значення змінній, ви змінюєте саму посилання, і це не впливає на оригінальний Об'єкт / Масив.

читати більше


6

Як ми знаємо, у масивах Javascript і об’єкти є посиланням, але якими способами ми можемо скопіювати масив, не змінюючи початковий масив на пізніший?

Ось кілька способів зробити це:

Уявіть, що у вашому коді є цей масив:

var arr = [1, 2, 3, 4, 5];

1) Прокручування масиву у функції та повернення нового масиву, наприклад:

 function newArr(arr) {
      var i=0, res = [];
      while(i<arr.length){
       res.push(arr[i]);
        i++;
       }
   return res;
 }

2) Використовуючи метод slice, фрагмент призначений для нарізання частини масиву, він буде фрагментувати частину масиву, не торкаючись оригіналу, в фрагменті, якщо не вказати початок і кінець масиву, він розріже весь масив і в основному зробити повну копію масиву, тому ми можемо легко сказати:

var arr2 = arr.slice(); // make a copy of the original array

3) Також контактний метод - це для об'єднання двох масивів, але ми можемо просто вказати один з масивів, і це в основному зробить копію значень у новому контактному масиві:

var arr2 = arr.concat();

4) Також метод раціоналізації та розбору не рекомендується, але це може бути простим способом копіювання масиву та об'єктів:

var arr2 = JSON.parse(JSON.stringify(arr));

5) Метод Array.from, це не підтримується широко, перед використанням перевірте підтримку в різних браузерах:

const arr2 = Array.from(arr);

6) ECMA6 спосіб, також не повністю підтримується, але babelJs можуть допомогти вам, якщо ви хочете перекласти:

const arr2 = [...arr];

6
let a = [1,2,3];

Тепер ви можете виконати будь-яке з наступних дій, щоб зробити копію масиву.

let b = Array.from(a); 

АБО

let b = [...a];

АБО

let b = new Array(...a); 

АБО

let b = a.slice(); 

АБО

let b = a.map(e => e);

Тепер, якщо я змінити,

a.push(5); 

Тоді, a є [1,2,3,5], але b все ще є [1,2,3], оскільки він має різні посилання.

Але я думаю, що у всіх методах вище Array.from краще і зроблено в основному для копіювання масиву.


1
який із них найшвидший?
Марк Кадр


4

У моєму конкретному випадку мені потрібно було забезпечити, щоб масив залишився цілим, щоб це працювало для мене:

// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}

2
+1 за те, що не додавати матовості у ваш код, зателефонувавши до функції, яка робить точно те саме, але менш очевидно. фрагмент може бути більш ефективним під кришкою, але для всіх, хто працює над кодом, це показує ваш намір. плюс це полегшує оптимізацію пізніше, якщо ви хочете (наприклад) фільтрувати те, що копіюєте. зауважте, однак це не обробляє глибоке копіювання, і ті ж внутрішні об'єкти передаються в новий масив за посиланням. Це може бути те, що ви хочете зробити, це не може.
несинхронізований

4

Зробіть копію багатовимірного масиву / об’єкта:

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

Завдяки Джеймсу Падолсі за цю функцію.

Джерело: Ось


3

Ден, не потрібно використовувати химерні трюки. Все, що вам потрібно зробити, це зробити копію arr1, зробивши це.

var arr2 = new Array(arr1);

Тепер arr1і arr2дві різні змінні масиву, що зберігаються в окремих стеках. Перевірте це на jsfiddle .


Це не копіює масив. Він створює масив з одним елементом, який посилається на оригінал (тобто var arr2 = [arr1];).
Тимофій003

3

Коли ми хочемо скопіювати масив за допомогою оператора призначення ( =), він не створює копію, він просто копіює покажчик / посилання на масив. Наприклад:

const oldArr = [1,2,3];

const newArr = oldArr;  // now oldArr points to the same place in memory 

console.log(oldArr === newArr);  // Points to the same place in memory thus is true

const copy = [1,2,3];

console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false

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

Способи копіювання масиву:

const oldArr = [1,2,3];

// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];

// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();

// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);

// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();

// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);

// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);

// For loop
function clone(base) {
	const newArray = [];
    for(let i= 0; i < base.length; i++) {
	    newArray[i] = base[i];
	}
	return newArray;
}

const newArr7 = clone(oldArr);

console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);

Будьте уважні, коли масиви або об’єкти вкладені !:

Коли вкладені масиви, значення копіюються посиланням. Ось приклад того, як це може призвести до проблем:

let arr1 = [1,2,[1,2,3]]

let arr2 = [...arr1];

arr2[2][0] = 5;  // we change arr2

console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference

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

Якщо ви хочете поглибити клавіші Java-масив, використовуйте JSON.parseразом із JSON.stringifyцим:

let arr1 = [1,2,[1,2,3]]

let arr2 = JSON.parse(JSON.stringify(arr1)) ;

arr2[2][0] = 5;

console.log(arr1);  // now I'm not modified because I'm a deep clone

Продуктивність копіювання:

Отже, який з них ми обираємо для оптимальної роботи. Виявляється, що найбільш багатослівний метод, forцикл має найвищі показники. Використовуйте forцикл для дійсно інтенсивного копіювання процесора (великий / багато масивів).

Після цього .slice()метод також має гідну продуктивність, а також менш довершений і простіший для впровадження програміста. Я пропоную використовувати .slice()для повсякденного копіювання масивів, які не дуже інтенсивні. Також уникайте використання JSON.parse(JSON.stringify(arr))(багато накладних витрат), якщо не потрібен глибокий клон, а продуктивність - це проблема.

Тест на продуктивність джерела


3

Якщо ваш масив містить елементи примітивного типу даних, такі як int, char, рядок тощо, ви можете скористатись одним із тих методів, який повертає копію вихідного масиву, наприклад .slice () або .map () або оператор розповсюдження ( завдяки ES6).

new_array = old_array.slice()

або

new_array = old_array.map((elem) => elem)

або

const new_array = new Array(...old_array);

АЛЕ якщо ваш масив містить складні елементи, такі як об’єкти (або масиви) або більше вкладених об'єктів , тоді вам доведеться переконатися, що ви робите копію всіх елементів від верхнього рівня до останнього рівня, що стосується іншого внутрішнього об'єкти будуть використовуватися, а це означає, що зміна значень у object_elements в new_array все одно вплине на old_array. Ви можете викликати цей метод копіювання на кожному рівні, роблячи повну копію з old_array.

Для глибокого копіювання ви можете використовувати вищезазначені методи для примітивних типів даних на кожному рівні залежно від типу даних або ви можете використовувати цей дорогий метод (згаданий нижче) для створення глибокої копії, не роблячи великої праці.

var new_array = JSON.parse(JSON.stringify(old_array));

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


Велике спасибі, ваша відповідь була єдиною, хто працював за моїм сценарієм,
albert sh

2

Якщо ви хочете зробити нову копію об'єкта або масиву, ви повинні явно скопіювати властивості об'єкта або елементів масиву, наприклад:

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

Ви можете шукати додаткову інформацію в Google про незмінні примітивні значення та посилання на об'єкти, що змінюються.


1
Не потрібно явно копіювати властивості об’єктів масиву. Дивіться відповідь Чтіві Малек.
Магне


2

Ви також можете скористатися оператором розповсюдження ES6 для копіювання масиву

var arr=[2,3,4,5];
var copyArr=[...arr];

2

Ось ще кілька способів копіювання:

const array = [1,2,3,4];

const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );



2

Короткі приклади:

  1. Якщо елементи масиву є примітивними типами (рядок, число тощо)

var arr1 = ['a','b','c'];
// arr1 and arr2 are independent and primitive elements are stored in 
// different places in the memory
var arr2 = arr1.slice(); 
arr2.push('d');
console.log(arr1); // [ 'a', 'b', 'c' ]
console.log(arr2); // [ 'a', 'b', 'c', 'd' ]

  1. Якщо елементи в масиві - це об'єктні літерали, інший масив ({}, [])

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
// arr1 and arr2 are independent and reference's/addresses are stored in different
// places in the memory. But those reference's/addresses points to some common place
// in the memory.
var arr2 = arr1.slice(); 
arr2.pop();      // OK - don't affect arr1 bcos only the address in the arr2 is
                 // deleted not the data pointed by that address
arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area 
                 // pointed by the addresses in both arr1 and arr2
arr2[1][0] = 9;	 // not OK - same above reason

console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]

  1. Рішення для 2 : Глибока копія по елементам

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
arr2 = JSON.parse(JSON.stringify(arr1));
arr2.pop();	  // OK - don't affect arr1
arr2[0].x = 'z';  // OK - don't affect arr1
arr2[1][0] = 9;	  // OK - don't affect arr1

console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]


1

Ось варіант:

var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
 *  arr1: a,b,c
 *  arr2: a,b,c,d
 */

не така погана ідея, хоча я б краще використати JSON stringify / parse замість eval, і все ж інше порівняння jsPerf було б добре перевірити, також зауважте, що toSourceце не стандартно і, наприклад, не працюватиме в Chrome.
dmi3y

1

Є нещодавно представлений Array.from, але, на жаль, на момент написання цього документа він підтримується лише в останніх версіях Firefox (32 і новіших версій). Його можна просто використовувати так:

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

Довідка: Тут

Або Array.prototype.mapможе використовуватися з функцією ідентичності:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

Довідка: Тут


+1 для згадування Array.from, який зараз підтримується у всіх основних браузерах, окрім Internet Explorer ( джерело ). .
mgthomas99

Будьте в курсі, вони Array.fromбудуть мутувати дані, не забезпечують глибокого копіювання / глибокого клонування.
Sudhansu Choudhary

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.