Вопрос: Копирование массива по значению в JavaScript


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

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

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


1304


источник


Ответы:


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

var newArray = oldArray.slice();

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

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

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


2111



В 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)
    myArray.splice(0), myArray.slice(), а также myArray.concat()методы могут быть использованы для глубоких копий массивов с буквальными значениями (булево, число и строка); где Slice имеет более высокую производительность, чем Concat ( http://jsperf.com/duplicate-array-slice-vs-concat/3 ).

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

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

    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, а не к   новый, независимый массив. Как я могу скопировать массив, чтобы получить два   независимые массивы?

Ответ

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

352



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

var arr2 = arr1.slice()

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

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

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


145



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

const itemsCopy = [...items];

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

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

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

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


129



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


66



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

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

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

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


37



Some of mentioned methods work well when working with simple data types like number or string, but when the array contains other objects these methods fail. When we try to pass any object from one array to another it is passed as a reference, not the object.

Add the following code in your JavaScript file:

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

And simply use

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

It will work.


17



From ES2015,

var arr2 = [...arr1];

14



I personally think Array.from is a more readable solution. By the way, just beware of its browser support.

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

13



If you are in an environment of ECMAScript 6, using the Spread Operator you could do it this way:

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>


10



Adding to the solution of array.slice(); be aware that if you have multidimensional array sub-arrays will be copied by references. What you can do is to loop and slice() each sub-array individually

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

same things goes to array of objects, they will be copied by reference, you have to copy them manually


9



As we know in Javascript arrays and objects are by reference, but what ways we can do copy the array without changing the original array later one?

Here are few ways to do it:

Imagine we have this array in your code:

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

1) Looping through the array in a function and return a new array, like this:

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

2) Using slice method, slice is for slicing part of the array, it will slice some part of your array without touching the original, in the slice, if don't specify the start and end of the array, it will slice the whole array and basically make a full copy of the array, so we can easily say:

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

3) Also contact method, this is for merging two array, but we can just specify one of arrays and then this basically make a copy of the values in the new contacted array:

var arr2 = arr.concat();

4) Also stringify and parse method, it's not recommended, but can be an easy way to copy Array and Objects:

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

5) Array.from method, this is not widely supported, before use check the support in different browsers:

const arr2 = Array.from(arr);

6) ECMA6 way, also not fully supported, but babelJs can help you if you want to transpile:

const arr2 = [...arr];

6