Copiando array por valor em JavaScript

Ao copiar uma matriz em JavaScript para outra matriz:

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

Eu percebi que arr2 se refere ao mesmo array que arr1 , ao invés de um novo array independente. Como posso copiar o array para obter dois arrays independentes?

Usa isto:

 var newArray = oldArray.slice(); 

Basicamente, a operação slice() clona a matriz e retorna a referência à nova matriz. Observe também que:

Para referências, strings e números (e não o object real), o slice copia referências de objects para o novo array. Tanto o array original quanto o novo se referem ao mesmo object. Se um object referenciado for alterado, as alterações ficarão visíveis para os arrays novos e originais.

Primitivos, como cordas e números, são imutáveis, portanto, alterações na cadeia ou no número são impossíveis.

Em Javascript, as técnicas de cópia profunda dependem dos elementos de uma matriz.
Vamos começar por aí.

Três tipos de elementos

Os elementos podem ser: valores literais, estruturas literais ou protótipos.

 // 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 () {}" 

A partir desses elementos, podemos criar três tipos de matrizes.

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

As técnicas de cópia profunda dependem dos três tipos de matriz

Com base nos tipos de elementos da matriz, podemos usar várias técnicas para copiar em profundidade.

Técnicas de cópia profunda em Javascript por tipos de elementos

  • Matriz de valores literais (tipo1)
    As myArray.splice(0) , myArray.slice() e myArray.concat() podem ser usadas para copiar arrays apenas com valores literais (boolean, number e string); onde Slice tem um desempenho maior que o Concat ( http://jsperf.com/duplicate-array-slice-vs-concat/3 ).

  • Matriz de valores literais (tipo1) e estruturas literais (tipo2)
    A JSON.parse(JSON.stringify(myArray)) pode ser usada para copiar literalmente valores literais (booleano, número, string) e estruturas literais (array, object), mas não objects protótipos.

  • Todos os arrays (type1, type2, type3)
    A técnica jQuery $.extend(myArray) pode ser usada para copiar profundamente todos os tipos de matriz. Bibliotecas como Underscore e Lo-dash oferecem funções de cópia profunda similares ao jQuery $.extend() , mas têm desempenho mais baixo. Mais surpreendentemente, $.extend() tem um desempenho maior do que a JSON.parse(JSON.stringify(myArray)) http://jsperf.com/js-deep-copy/15 .
    E para aqueles desenvolvedores que fogem de bibliotecas de terceiros (como o jQuery), você pode usar a seguinte function personalizada; que tem desempenho superior a $ .extend e copia em profundidade todos os arrays.

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

Então, para responder a pergunta …

Questão

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

Eu percebi que arr2 se refere ao mesmo array que arr1, ao invés de um novo array independente. Como posso copiar o array para obter dois arrays independentes?

Responda

Como arr1 é uma matriz de valores literais (booleano, número ou string), você pode usar qualquer técnica de cópia detalhada discutida acima, em que slice tenha o maior desempenho.

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

Nenhum jQuery necessário … Exemplo de trabalho

 var arr2 = arr1.slice() 

Isso copia a matriz da posição inicial de 0 até o final da matriz.

É importante observar que ele funcionará como esperado para tipos primitivos (string, number, etc.) e também para explicar o comportamento esperado para tipos de referência …

Se você tiver uma matriz de tipos de referência, digamos do tipo Object . A matriz será copiada, mas ambas as matrizes conterão referências aos mesmos Object . Portanto, nesse caso, parece que a matriz é copiada por referência, embora a matriz seja realmente copiada.

Você pode usar propagações de matriz ... para copiar matrizes.

const itemsCopy = [...items];

Além disso, se você quiser criar um novo array com o existente sendo parte dele:

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

As propagações de matriz agora são suportadas em todos os principais navegadores, mas se você precisar de suporte mais antigo, use typescript ou babel e compile para ES5.

Mais informações sobre spreads

Uma alternativa ao slice é concat , que pode ser usada de duas maneiras. O primeiro deles é talvez mais legível, pois o comportamento pretendido é muito claro:

 var array2 = [].concat(array1); 

O segundo método é:

 var array2 = array1.concat(); 

Cohen (nos comentários) apontou que este último método tem melhor desempenho .

A maneira como isso funciona é que o método concat cria um novo array que consiste nos elementos do object no qual ele é chamado, seguidos pelos elementos de quaisquer arrays passados ​​para ele como argumentos. Então, quando nenhum argumento é passado, simplesmente copia o array.

Lee Penkman, também nos comentários, aponta que, se houver uma chance de array1 ser undefined , você pode retornar uma matriz vazia da seguinte forma:

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

Ou, para o segundo método:

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

Note que você também pode fazer isso com slice : var array2 = (array1 || []).slice(); .

Foi assim que fiz depois de tentar muitas abordagens:

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

Isso criará uma nova cópia não relacionada à primeira (não uma cópia superficial).

Também isso obviamente não irá clonar events e funções, mas a coisa boa que você pode fazer em uma linha, e pode ser usado para qualquer tipo de object (arrays, strings, números, objects …)

Alguns dos methods mencionados funcionam bem ao trabalhar com tipos de dados simples, como number ou string, mas quando o array contém outros objects, esses methods falham. Quando tentamos passar qualquer object de um array para outro, ele é passado como referência, não como object.

Adicione o seguinte código no seu arquivo 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; }; 

E simplesmente use

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

Vai funcionar.

A partir do ES2015,

 var arr2 = [...arr1]; 

Eu pessoalmente acho que o Array.from é uma solução mais legível. By the way, apenas tenha cuidado com o suporte do navegador.

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

Se você estiver em um ambiente de ECMAScript 6 , usando o Operador de Spread, você pode fazer isso da seguinte maneira:

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

Adicionando à solução de array.slice (); esteja ciente de que, se você tiver matrizes de multidimensional array, as sub-matrizes serão copiadas por referências. O que você pode fazer é fazer o loop e fatiar () cada sub-array individualmente

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

mesmas coisas vão para array de objects, elas serão copiadas por referência, você tem que copiá-las manualmente

Como sabemos em JavaScript arrays e objects são por referência, mas que maneiras podemos copiar o array sem alterar o array original mais tarde?

Aqui estão algumas maneiras de fazer isso:

Imagine que temos esse array no seu código:

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

1) Looping através da matriz em uma function e retornar uma nova matriz, assim:

  function newArr(arr) { var i=0, res = []; while(i 

2) Usando o método de fatia, fatia é para fatiar parte da matriz, ele irá cortar alguma parte da sua matriz sem tocar o original, na fatia, se não especificar o início e o fim da matriz, ele irá dividir o todo matriz e, basicamente, fazer uma cópia completa da matriz, para que possamos dizer facilmente:

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

3) Também entre em contato com o método, isso é para mesclar duas matriz, mas podemos apenas especificar uma das matrizes e, em seguida, isso basicamente fazer uma cópia dos valores na nova matriz contatada:

 var arr2 = arr.concat(); 

4) Também stringify e analisar o método, não é recomendado, mas pode ser uma maneira fácil de copiar Array e Objects:

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

5) Array.from método, isso não é amplamente suportado, antes de usar verificar o suporte em diferentes navegadores:

 const arr2 = Array.from(arr); 

6) ECMA6 caminho, também não totalmente suportado, mas babelJs pode ajudá-lo se você quiser transpilar:

 const arr2 = [...arr]; 

Dan, não precisa usar truques extravagantes. Tudo o que você precisa fazer é copiar o arr1 fazendo isso.

 var arr2 = new Array(arr1); 

No meu caso particular, eu precisava garantir que o array permanecesse intacto, então isso funcionou para mim:

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

Faça cópia da matriz / object multidimensional:

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

Obrigado a James Padolsey por essa function.

Fonte: aqui

Se você quiser fazer uma nova cópia de um object ou matriz, copie explicitamente as propriedades do object ou os elementos da matriz, por exemplo:

 var arr1 = ['a','b','c']; var arr2 = []; for (var i=0; i < arr1.length; i++) { arr2[i] = arr1[i]; } 

Você pode pesquisar mais informações no Google sobre valores primitivos imutáveis ​​e referências de objects mutáveis.

Usando jQuery, a cópia profunda pode ser feita da seguinte maneira:

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

Você também pode usar o operador de dispersão ES6 para copiar o Array

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

Aqui estão mais algumas maneiras de copiar:

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

Quando queremos copiar uma matriz usando o operador de atribuição ( = ), ela não cria uma cópia, apenas copia o ponteiro / referência para a matriz. Por exemplo:

 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 

Aqui está uma variante:

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

Há o recém introduzido Array.from , mas infelizmente, até o momento desta publicação, ele é suportado apenas em versões recentes do Firefox (32 e superior). Pode ser usado simplesmente da seguinte forma:

 var arr1 = [1, 2, 3]; console.log(Array.from(arr1)); // Logs: [1, 2, 3] 

Referência: aqui

Ou Array.prototype.map pode ser usado com uma function de identidade:

 function identity(param) { return param; } var arr1 = [1, 2, 3], clone = arr1.map(identity); 

Referência: aqui

Você poderia usar o ES6 com o Opeartor de propagação, é mais simples.

 arr2 = [...arr1]; 

Há limitações..verifique docs Espalhe a syntax @ mozilla

Para objects contendo matriz ES6

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

Você pode fazer isso da seguinte maneira:
arr2 = arr1.map(x => Object.assign({}, x));

Importante!

A maioria das respostas aqui funciona para casos particulares .

Se você não se importa com o uso de objects profundos / nesteds e adereços ( ES6 ):

let clonedArray = [...array]

mas se você quiser fazer um clone profundo use isto:

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