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

При копировании массива в JavaScript в другой массив:

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

Я понял, что arr2 относится к тому же массиву, что и arr1, а не к новому независимому массиву. Как скопировать массив, чтобы получить два независимых массива?

Ответ 1

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

var newArray = oldArray.slice();

По сути, операция slice() клонирует массив и возвращает ссылку на новый массив.

Также обратите внимание, что:

Для ссылок, строк и чисел (а не для реального объекта) slice() копирует ссылки на объекты в новый массив. И исходный, и новый массив ссылаются на один и тот же объект. Если ссылочный объект изменяется, изменения видны как новому, так и исходному массивам.

Примитивы, такие как строки и числа, являются неизменными, поэтому изменения строки или числа невозможны.

Ответ 2

В 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 deep copy techniques by element types

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

  • Массив литеральных значений (тип1) и литеральных структур (тип2)
    JSON.parse(JSON.stringify(myArray)) может использоваться для глубокого копирования литеральных значений (логических, чисел, строк) и литеральных структур (массив, объект), но не объектов-прототипов.

  • Все массивы (тип1, тип2, тип3)
    Метод 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

Ответ 3

Вы можете использовать массивные спреды ... для копирования массивов.

const itemsCopy = [...items];

Также, если вы хотите создать новый массив с существующим, являющимся его частью:

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

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

Дополнительная информация о спредах

Ответ 4

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

var arr2 = arr1.slice()

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

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

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

Ответ 5

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

var array2 = [].concat(array1);

Второй способ:

var array2 = array1.concat();

Коэн (в комментариях) указал, что этот последний метод имеет лучшую производительность.

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

Ли Пенкман, также в комментариях, указывает, что если есть шанс array1 undefined, вы можете вернуть пустой массив следующим образом:

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

Или, для второго метода:

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

Обратите внимание, что вы также можете сделать это с помощью slice: var array2 = (array1 || []).slice();.

Ответ 6

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

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

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

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

Ответ 7

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

Добавьте следующий код в свой файл 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()

Он будет работать.

Ответ 8

Из ES2015,

var arr2 = [...arr1];

Ответ 9

Я лично считаю 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);

Ответ 10

Важный!

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

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

let clonedArray = [...array]

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

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


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

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>

Ответ 12

Добавляя к решению array.slice();, помните, что если у вас есть многомерный массив, подмассивы будут скопированы по ссылкам. То, что вы можете сделать, это loop и 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]);

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

Ответ 13

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

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

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

var arr2 = arr.concat();

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

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

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

const arr2 = Array.from(arr);

6) ECMA6, также не полностью поддерживается, но babelJs может помочь вам, если вы хотите перекрыть:

const arr2 = [...arr];

Ответ 14

В моем конкретном случае мне нужно было убедиться, что массив остался нетронутым, поэтому это сработало для меня:

// 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]);
}

Ответ 15

Сделать копию многомерного массива/объекта:

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;
}

Спасибо Джеймсу Падолси за эту функцию.

Источник: Здесь

Ответ 16

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 лучше и сделан в основном для копирования массива.

Ответ 17

Дэн, не нужно использовать причудливые трюки. Все, что вам нужно сделать, это сделать копию arr1, выполнив это.

var arr2 = new Array(arr1);

Ответ 18

Когда мы хотим скопировать массив с помощью оператора присваивания (=), он не создает копию, он просто копирует указатель/ссылку на массив. Например:

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

Ответ 19

Если ваш массив содержит элементы примитивного типа данных, такие как 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);

НО, если ваш массив содержит сложные элементы, такие как объекты (или массивы) или более вложенные объекты, тогда вам нужно будет убедиться, что вы делаете копию всех элементов с верхнего уровня до последнего уровня. объекты будут использоваться, и это означает, что изменение значений в объектных_имениках в new_array все равно будет влиять на old_array. Вы можете вызвать этот метод копирования на каждом уровне, сделав DEEP COPY для old_array.

Для глубокого копирования вы можете использовать вышеупомянутые методы для примитивных типов данных на каждом уровне в зависимости от типа данных, или вы можете использовать этот дорогостоящий метод (упомянутый ниже) для создания глубокой копии без большой работы.

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

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

Ответ 20

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

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

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

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

Ответ 21

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

var arr2 = $.extend(true, [], arr1);

Ответ 22

Вы также можете использовать оператор распространения ES6 для копирования Array

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

Ответ 23

Вот еще несколько способов копирования:

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

const arrayCopy1 = Object.values ​​(массив);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = ​​array.map(i = > i);
const arrayCopy4 = Array.of(... array);

Ответ 24

Быстрые примеры:

  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' ]

Ответ 25

Здесь вариант:

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
 */

Ответ 26

Там появился новый 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);

Ссылка: Здесь

Ответ 27

Вы можете сделать это следующим образом:
arr2 = arr1.map(x => Object.assign({}, x));

Ответ 29

Вот как вы можете сделать это для массива массивов примитивов переменной глубины:

// If a is array: 
//    then call cpArr(a) for each e;
//    else return a

const cpArr = a => Array.isArray(a) && a.map(e => cpArr(e)) || a;

let src = [[1,2,3], [4, ["five", "six", 7], true], 8, 9, false];
let dst = cpArr(src);

https://jsbin.com/xemazog/edit?js,console

Ответ 30

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

Так, как мы копируем этот 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';

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

прочитайте больше