Копировать массив по значению

1745

При копировании массива в 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
jsperf.com/flat-array-copy
Уолл Кирилл
Для этого массива (который содержит примитивные строки) вы можете использовать его var arr2 = arr1.splice();для глубокого копирования, но этот метод не будет работать, если элементы в вашем массиве содержат литеральные структуры (т.е. []или {}) или объекты-прототипы (то есть function () {}, newи т. Д.). Смотрите мой ответ ниже для дальнейших решений.
tfmontague
16
Сейчас 2017 год, поэтому вы можете рассмотреть возможность использования функций ES6: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich

Ответы:

2702

Использовать этот:

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 (); // клонировать oldArray в newArray
dudewad
12
@ GáborImre Я понял, конечно. Но ответить на конкретную инженерную проблему, включив целую библиотеку, на мой взгляд, не полезно, это раздувание дизайна. Я вижу, что разработчики делают это много, и затем вы получаете проект, который включает в себя всю инфраструктуру вместо того, чтобы писать одну функцию. Только мой МО, хотя.
dudewad
5
Извлеченный урок: не путайте .slice()с .splice(), что дает пустой массив. Большая разница.
сумасшедший
531

В 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)
    Этот 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
tfmontague
источник
1
Многие из этих подходов не работают. Использование оператора присваивания означает, что вы должны переназначить исходное буквальное значение arr1. Это очень редко, что это будет иметь место. Использование spliceстирает arr1, так что это не копия вообще. Использование не JSONбудет выполнено, если какое-либо из значений в массиве является Функцией или имеет прототипы (например, a Date).
Данкрамб
Использование сращивания является частичным решением. Это потерпит неудачу при гораздо большем количестве случаев, чем JSON. Splice создает глубокую копию строк и чисел, когда перемещает значения - никогда не говорил, что возвращает копию.
tfmontague
1
Почему сплайс (0)? Разве это не должно быть slice ()? Я думаю, что он не должен изменять исходный массив, что делает сплайсинг. @JamesMontagne
helpse
2
splice создаст указатели на элементы в исходном массиве (мелкая копия). splice (0) выделит новую память (глубокое копирование) для элементов в массиве, которые являются числами или строками, и создаст указатели для всех других типов элементов (поверхностное копирование). Передав начальное значение, равное нулю, функциональному методу сращивания, он не будет склеивать какие-либо элементы из исходного массива и, следовательно, не изменяет его.
tfmontague
1
На самом деле, существует только один тип массива: массив «чего-то». Нет никакой разницы между [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'];

Распространения массива теперь поддерживаются во всех основных браузерах, но если вам нужна более старая поддержка, используйте машинопись или babel и компилируйте в ES5.

Больше информации о спредах

Люк Фемур
источник
1
Это не будет работать для глубокого копирования. Глубокое клонирование массива в JavaScript .
SNag
151

Нет необходимости jQuery ... Рабочий пример

var arr2 = arr1.slice()

Это копирует массив из начальной позиции 0до конца массива.

Важно отметить, что он будет работать как ожидается для примитивных типов (строка, число и т. Д.), А также объяснить ожидаемое поведение для ссылочных типов ...

Если у вас есть массив ссылочных типов, скажем, типа Object. Массив будет скопирован, но оба массива будут содержать ссылки на одинаковые Object. Таким образом, в этом случае может показаться, что массив копируется по ссылке, хотя массив фактически копируется.

jondavidjohn
источник
12
Нет, это не будет глубокой копией.
jondavidjohn
Попробуй это; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh
2
В чем разница между этим ответом и принятым ответом?
Исаак Пак
получение ошибки в консоли для вашего данного примера «Ошибка типа: 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();.

Ninjakannon
источник
31
На самом деле вы также можете сделать: var array2 = array1.concat (); Это намного быстрее в отношении производительности. (JSPerf: jsperf.com/copy-simple-array и jsperf.com/copy-array-slice-vs-concat/5
Коэн,
5
Стоит отметить, что если array1 не является массивом, то [].concat(array1)возвращается, [array1]например, если он не определен, вы получите [undefined]. Я иногда делаюvar array2 = [].concat(array1 || []);
Ли Пенкман
60

Вот как я сделал это после попытки многих подходов:

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

Это создаст новую глубокую копию, не связанную с первой (не мелкую копию).

Также это, очевидно, не будет клонировать события и функции, но хорошо, что вы можете сделать это в одну строку, и это может быть использовано для любого типа объекта (массивы, строки, числа, объекты ...)

Читиви Малек
источник
4
Это самый лучший. Я использую тот же метод давным-давно и думаю, что в рекурсивных циклах старой школы больше нет смысла
Владимир Харлампиди
1
Имейте в виду, что эта опция плохо обрабатывает графоподобные структуры: происходит сбой при наличии циклов и не сохраняет общие ссылки.
Рубен
1
Это также не подходит для таких вещей, как Date, или, действительно, для всего, что имеет прототип. Кроме того, undefineds конвертируются в nulls.
Данкрамб
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: превышен максимальный размер стека вызовов»
sawe
1
Мои извинения, эта ошибка возникает в chrome, если arr1 не объявлен. поэтому я скопировал приведенный выше код и получил ошибку, однако, если я объявляю массив arr1, то я не получаю ошибку. Вы можете улучшить ответ, объявив arr1 чуть выше arr2, я вижу, что довольно много «нас» там, кто не признал, что мы должны объявить arr1 (отчасти потому, что когда я оценивал ваш ответ, я был в спешке и нужно что-то, что «просто работает»)
sawe
.slice()все еще работает нормально, даже если у вас есть объекты в вашем массиве: jsfiddle.net/edelman/k525g
Джейсон
7
@ Джейсон, но объекты все еще указывают на один и тот же объект, поэтому изменение одного изменит другой. jsfiddle.net/k525g/1
Сэмюэль
Отличный код. Один вопрос, который у меня возник, на самом деле я пытался скопировать один массив в другой, как этот var arr1 = new Array (), а затем var arr2 = arr1; Если я что-то изменяю в arr2, то изменение происходит и с arr1. Однако, если я использую созданный вами прототип клона, он фактически создает новый экземпляр этого массива или, другими словами, копирует его. Так это проблема с браузером? или javascript по умолчанию устанавливает две переменные одной указывающей на другую с помощью указателей, когда кто-то делает var arr2 = arr1 и почему не происходит с целочисленными переменными? см. jsfiddle.net/themhz/HbhtA
themhz
17

От ES2015,

var arr2 = [...arr1];
Бупати Раджаа
источник
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()Решение полностью неинтуитивное. Спасибо за это.
Banago
15

Важный!

Большинство ответов здесь работает для конкретных случаев .

Если вам не нужны глубокие / вложенные объекты и реквизит ( ES6 ):

let clonedArray = [...array]

но если вы хотите сделать глубокий клон, используйте вместо этого:

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


Для пользователей lodash:

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

а также

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

ulou
источник
11

Если вы находитесь в среде ECMAScript 6 , используя Spread Operator, вы можете сделать это следующим образом:

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>

Уолтер Чапилликен - wZVanG
источник
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]);

То же самое относится к массиву объектов, они будут скопированы по ссылке, вы должны скопировать их вручную

A.Zaben
источник
Этот ответ заслуживает места в верхней части страницы! Я работал с многомерными вложенными массивами и не мог понять, почему внутренние массивы всегда копировались с помощью ref, а не val. Эта простая логика решила мою проблему. Я бы дал вам +100, если это возможно!
Mac
8

Примитивные значения всегда передаются по его значению (копируются). Однако составные значения передаются по ссылке.

Так, как мы копируем этот arr?

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

Скопируйте массив в ES6

let arrCopy = [...arr]; 

Скопируйте Массив в ES5

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

Почему `let arrCopy = arr` не передается по значению?

Передача одной переменной в другую по составным значениям, таким как объект / массив, ведет себя по-разному. Используя оператор asign для значений copand, мы передаем ссылку на объект. Вот почему значение обоих массивов изменяется при удалении / добавлении элементов arr.

Исключения:

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

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

читать далее

DevWL
источник
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, slice предназначен для нарезки части массива, он будет нарезать некоторую часть вашего массива, не касаясь оригинала, в срезе, если не указать начало и конец массива, он будет нарезать весь массив массив и в основном сделать полную копию массива, поэтому мы можем легко сказать:

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

3) Также контактный метод, это для объединения двух массивов, но мы можем просто указать один из массивов, и тогда это в основном сделает копию значений в новом контактируемом массиве:

var arr2 = arr.concat();

4) Также метод stringify и parse, не рекомендуется, но может быть простым способом скопировать Array и Objects:

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
какой из них самый быстрый?
Марк Кадр
5

Я бы лично предпочел такой способ:

JSON.parse(JSON.stringify( originalObject ));
Diemauerdk
источник
Так какой путь предлагается здесь ?
Script47
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 за то, что вы не добавили мрачность в свой код, вызвав функцию, которая делает то же самое, но менее очевидным способом. slice может быть более эффективным под капотом, но для всех, кто работает над кодом, это показывает ваше намерение. Кроме того, это упрощает оптимизацию позже, если вы хотите (например) фильтровать то, что копируете. обратите внимание, однако, что это не обрабатывает глубокое копирование, и те же самые внутренние объекты передаются в новый массив, по ссылке. Это может быть то, что вы хотите сделать, а может и нет.
несинхронизировано
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 .

DragoRaptor
источник
Это не копирует массив. Он создает массив с одним элементом, который ссылается на оригинал (то есть var arr2 = [arr1];).
Timothy003
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

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

Если вы хотите глубокое клонирование массива javascript, используйте 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 или string и т. Д. То вы можете использовать один из тех методов, который возвращает копию исходного массива, например .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));

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

abhinav1602
источник
Большое спасибо, ваш ответ был единственным, кто работал для моего сценария,
Альберт
2

Если вы хотите создать новую копию объекта или массива, вы должны явно скопировать свойства объекта или элементов массива, например:

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

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

Вы можете найти в Google дополнительную информацию об неизменных значениях примитивов и ссылках на изменяемые объекты.

Sotiris
источник
1
Вам не нужно явно копировать свойства объектов массива. Смотрите ответ Чтиви Малек.
Магне
2

Используя глубокое копирование jQuery, можно сделать следующее:

var arr2 = $.extend(true, [], arr1);
Александр Цурика
источник
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 ] ]

SridharKritha
источник
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
 */
Zyox
источник
неплохая идея, хотя я бы лучше использовал 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 что данные будут видоизменяться, не обеспечивает глубокого копирования / глубокого клонирования.
Судхансу Чоудхари
1

Для массива ES6, содержащего объекты

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
askilondz
источник