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

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

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

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

+1570
20 сент. '11 в 13:38
источник поделиться
30 ответа
  • 1
  • 2

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

var newArray = oldArray.slice();

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

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

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

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

+2501
20 сент. '11 в 13:41
источник

В 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
+464
08 мая '14 в 8:36
источник

Связанные вопросы


Похожие вопросы

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

const itemsCopy = [...items];

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

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

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

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

+178
03 июл. '15 в 14:46
источник

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

var arr2 = arr1.slice()

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

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

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

+150
20 сент. '11 в 13:39
источник

Альтернативой 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();.

+71
18 окт. '12 в 0:11
источник

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

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

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

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

+52
23 апр. '14 в 13:32
источник

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

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

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

+19
12 дек. '12 в 16:22
источник

Из ES2015,

var arr2 = [...arr1];
+15
24 дек. '15 в 7:14
источник

Я лично считаю 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);
+14
09 мар. '16 в 12:01
источник

Важный!

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

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

let clonedArray = [...array]

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

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


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

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

а также

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

+13
22 авг. '18 в 14:46
источник

Если вы находитесь в среде 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>
+11
03 июл. '15 в 9:31
источник

Добавляя к решению 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]);

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

+9
15 апр. '14 в 13:40
источник

Как мы знаем в массивах 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];
+6
26 мар. '17 в 2:35
источник

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

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

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

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

+4
22 июл. '14 в 12:15
источник

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

var arr2 = new Array(arr1);

Теперь arr1 и arr2 - две разные переменные массива, хранящиеся в отдельных стеках. Проверьте это на jsfiddle.

+4
18 мая '18 в 15:05
источник

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

// 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]);
}
+4
13 мая '14 в 18:20
источник

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

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

+3
26 сент. '18 в 13:52
источник

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

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
06 авг. '18 в 10:06
источник

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

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

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

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

+2
20 сент. '11 в 13:45
источник
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 лучше и сделан в основном для копирования массива.

+2
18 нояб. '18 в 6:43
источник

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

  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 ] ]
+2
11 сент. '18 в 13:41
источник

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

  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);
+2
12 апр. '18 в 7:00
источник

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

var arr=[2,3,4,5];
var copyArr=[...arr];
+2
30 июн. '17 в 19:16
источник

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

var arr2 = $.extend(true, [], arr1);
+2
12 янв. '17 в 19: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);

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

+1
10 дек. '14 в 15:45
источник

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

+1
31 мая '18 в 11:50
источник

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

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
 */
+1
06 окт. '13 в 6:36
источник

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

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

+1
13 дек. '18 в 11:10
источник

Вы можете использовать ES6 с распространенным Opeartor, его проще.

arr2 = [...arr1];

Существуют ограничения..check docs Распространение синтаксиса @mozilla

+1
27 июн. '18 в 9:06
источник

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

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

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

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

+1
31 дек. '19 в 1:28
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос