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

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

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

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

28 ответов


используйте этот:

var newArray = oldArray.slice();

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

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

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


в Javascript методы глубокого копирования зависят от элементов массива.
Начнем с этого.

три типа элементов

элементы могут быть: литеральные значения, литеральные структуры или прототипы.

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

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

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

из этих элементов мы можем создать три типа массивов.

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

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

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

методы глубокого копирования зависят от трех типов массива

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

Javascript deep copy techniques by element types

  • массив литеральных значений (type1)
    The myArray.splice(0), myArray.slice() и myArray.concat() методы могут использоваться для глубокого копирования массивов только с литеральными значениями (boolean, number и string); где Slice имеет более высокую производительность, чем Concat (http://jsperf.com/duplicate-array-slice-vs-concat/3).

  • массив буквального значения (type1) и литерал-структуры (type2)
    The JSON.parse(JSON.stringify(myArray)) техника может быть использована для глубокого копирования литеральных значений (boolean, number, string) и литеральных структур (array, object), но не объектов-прототипов.

  • все массивы (type1, type2, type3)
    В jQuery $.extend(myArray) техника может использоваться для глубокого копирования всех типов массивов. Библиотеки, как подчеркивание и Ло-тире предложите подобные функции глубок-экземпляра к в jQuery $.extend(), но имеют более низкую производительность. Более удивительно, $.extend() имеет высокую производительность, чем JSON.parse(JSON.stringify(myArray)) техника http://jsperf.com/js-deep-copy/15.
    И для тех разработчиков, которые уклоняются от сторонних библиотек (например, jQuery), вы можете использовать следующие пользовательские функции, которое имеет более высокую производительность, чем $.extend и deep-копирует все массивы.

    function copy(o) {
      var output, v, key;
      output = Array.isArray(o) ? [] : {};
      for (key in o) {
        v = o[key];
        output[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
      }
      return output;
    }  
    

так ответить вопрос...

вопрос

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

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

ответ

, потому что arr1 представляет собой массив литеральных значений (boolean, number или string), вы можете использовать любой метод глубокой копии, описанный выше, где slice имеет самый высокий спектакль.

// Highest performance for deep copying literal values
arr2 = arr1.slice();

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
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

вы можете использовать Array spreads ... копировать массивы.

const itemsCopy = [...items];

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

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

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

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


не требуется jQuery... Пример Работающего

var arr2 = arr1.slice()

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

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

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


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

var array2 = [].concat(array1);

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

var array2 = array1.concat();

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

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

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

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

или, для второго способа:

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

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


вот как я это сделал, попробовав много подходов:

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

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

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


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

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

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


из ES2015,

var arr2 = [...arr1];

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

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

Если вы находитесь в среде ECMAScript 6, С помощью Распространение Оператор вы могли бы сделать это таким образом:

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>

добавление к решению массив.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]);

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


важно!

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

если вы не заботитесь о глубоких / вложенных объектов и реквизит использовать (ЕС6):

let clonedArray = [...array]

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

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


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

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

и

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


как мы знаем в 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 и метод разбора, это не рекомендуется, но может быть простым способом копирования массива и объектов:

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

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

const arr2 = Array.from(arr);

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

const arr2 = [...arr];

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

var arr2 = new Array(arr1);

Теперь arr1 и arr2 две разные переменные массива хранятся в отдельных штабелях. проверьте это на jsfiddle.


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

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

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

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

спасибо Джеймс Padolsey для этой функции.

источник: здесь


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

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

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

если вы хотите deepclone массив 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 loop имеет самую высокую производительность. Используйте for цикл для действительно интенсивного копирования процессора (большие/многие массивы).

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

исходный тест производительности


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

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

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

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


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

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

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

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

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

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

вот такой вариант:

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

вот недавно представленный 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);

ссылки: здесь


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

arr2 = [...arr1];

есть ограничения..проверить документы синтаксис распространения @ mozilla


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

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

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

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}

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


если Ваш массив содержит элементы примитивный тип данных например int, char, или строка etc затем вы можете использовать один из тех методов, который возвращает копию исходного массива, например .ломтик или. 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));

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