Скопировать массив по значению - PullRequest
1575 голосов
/ 20 сентября 2011

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

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

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

Ответы [ 33 ]

2508 голосов
/ 20 сентября 2011

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

var newArray = oldArray.slice();

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

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

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

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

467 голосов
/ 08 мая 2014

В 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

  • Массив литеральных значений (тип1)
    Методы [...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
178 голосов
/ 03 июля 2015

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

const itemsCopy = [...items];

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

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

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

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

150 голосов
/ 20 сентября 2011

Не требуется jQuery ... Рабочий пример

var arr2 = arr1.slice()

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

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

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

71 голосов
/ 18 октября 2012

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

52 голосов
/ 23 апреля 2014

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

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

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

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

19 голосов
/ 12 декабря 2012

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

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

Будет работать.

15 голосов
/ 09 марта 2016

Лично я считаю 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);
15 голосов
/ 24 декабря 2015

от ES2015,

var arr2 = [...arr1];
13 голосов
/ 22 августа 2018

Важно!

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

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

let clonedArray = [...array]

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

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


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

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

и

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

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