Copia l'array per valore


1745

Quando si copia un array in JavaScript su un altro array:

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

Mi sono reso conto che si arr2riferisce allo stesso array arr1piuttosto che a un nuovo array indipendente. Come posso copiare l'array per ottenere due array indipendenti?


3
Sembra che attualmente in Chrome 53 e Firefox 48 abbiamo ottime prestazioni slicee spliceoperazioni e un nuovo operatore di diffusione e Array.fromun'implementazione molto più lenta. Guarda perfjs.fnfo
Pencroff il

jsben.ch/#/wQ9RU <= questo benchmark fornisce una panoramica dei diversi modi di copiare un array
EscapeNetscape


Per questo array (quello che contiene stringhe primitivi) è possibile utilizzare var arr2 = arr1.splice();per copia completa, ma questa tecnica non funzionerà se gli elementi a propria matrice contenere strutture letterali (es []o {}) o oggetti prototipo (cioè function () {}, new, ecc). Vedi la mia risposta di seguito per ulteriori soluzioni.
tfmontague,

16
È il 2017, quindi potresti prendere in considerazione l'utilizzo delle funzionalità ES6: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich,

Risposte:


2701

Usa questo:

var newArray = oldArray.slice();

Fondamentalmente, l' slice()operazione clona l'array e restituisce un riferimento a un nuovo array.

Si noti inoltre che:

Per riferimenti, stringhe e numeri (e non l'oggetto reale), slice()copia i riferimenti agli oggetti nel nuovo array. Sia l'array originale che il nuovo si riferiscono allo stesso oggetto. Se un oggetto referenziato cambia, le modifiche sono visibili sia alla matrice nuova che a quella originale.

Le primitive come stringhe e numeri sono immutabili, quindi le modifiche alla stringa o al numero sono impossibili.


9
Per quanto riguarda le prestazioni, i seguenti test jsPerf mostrano che var arr2 = arr1.slice () è veloce quanto var arr2 = arr1.concat (); JSPerf: jsperf.com/copy-array-slice-vs-concat/5 e jsperf.com/copy-simple-array . Il risultato di jsperf.com/array-copy/5 mi ha sorpreso al punto che mi chiedo se il codice del test sia valido.
Cohen,

94
Anche se questo ha già ricevuto un sacco di voti, ne merita un altro perché descrive correttamente i riferimenti in JS, che è un po 'raro, sfortunatamente.
Wayne,

34
@ GáborImre aggiungeresti un'intera libreria semplicemente per leggibilità? Veramente? Aggiungerei un commento se fossi così preoccupato per la leggibilità. Vedi: var newArray = oldArray.slice (); // Clona oldArray in newArray
dudewad,

12
@ GáborImre Lo capisco, certo. Ma rispondere a un problema di ingegneria specifico includendo a mio parere un'intera libreria non è utile, è gonfio di design. Vedo che gli sviluppatori lo fanno molto e poi si finisce con un progetto che includeva un intero framework per sostituire la necessità di scrivere una singola funzione. Solo il mio MO, però.
Dudewad,

5
Lezione appresa: non confondere .slice()con .splice(), che ti dà una matrice vuota. Grande differenza.
crazypeter,

531

In Javascript, le tecniche di copia profonda dipendono dagli elementi di un array. Cominciamo da lì.

Tre tipi di elementi

Gli elementi possono essere: valori letterali, strutture letterali o prototipi.

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

Da questi elementi possiamo creare tre tipi di array.

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

Le tecniche di copia profonda dipendono dai tre tipi di array

In base ai tipi di elementi nell'array, possiamo usare varie tecniche per la copia approfondita.

Tecniche di copia profonda Javascript per tipi di elementi

  • Matrice di valori letterali (type1)
    Il [...myArray], myArray.splice(0), myArray.slice(), e myArray.concat()tecniche possono essere utilizzate per array copia profonda con valori letterali (boolean, numero e stringa) solo; dove l'operatore Spread [...myArray]ha le migliori prestazioni ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).

  • Matrice di valori letterali (tipo1) e strutture letterali (tipo2)
    La JSON.parse(JSON.stringify(myArray))tecnica può essere utilizzata per copiare in profondità valori letterali (booleani, numeri, stringhe) e strutture letterali (matrice, oggetto), ma non oggetti prototipo.

  • Tutti gli array (tipo1, tipo2, tipo3)
    La $.extend(myArray)tecnica jQuery può essere utilizzata per copiare in profondità tutti i tipi di array. Librerie come Underscore e Lo-dash offrono funzioni simili di copia profonda di jQuery $.extend() , ma offrono prestazioni inferiori. Più sorprendentemente, $.extend()ha prestazioni più elevate rispetto alla JSON.parse(JSON.stringify(myArray))tecnica http://jsperf.com/js-deep-copy/15 .
    E per quegli sviluppatori che evitano le librerie di terze parti (come jQuery), puoi usare la seguente funzione personalizzata; che ha prestazioni superiori a $ .extend e copia in profondità tutti gli array.

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

Quindi per rispondere alla domanda ...

Domanda

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

Mi sono reso conto che arr2 si riferisce allo stesso array di arr1, piuttosto che a un nuovo array indipendente. Come posso copiare l'array per ottenere due array indipendenti?

Risposta

Poiché arr1è un array di valori letterali (booleano, numero o stringa), è possibile utilizzare qualsiasi tecnica di copia approfondita discussa sopra, in cui l'operatore di diffusione ...ha le massime prestazioni.

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

1
Molti di questi approcci non funzionano bene. L'uso dell'operatore di assegnazione significa che è necessario riassegnare il valore letterale originale di arr1. È molto raro che sia così. Usare le splicecancellazioni arr1, quindi non è affatto una copia. L'uso JSONnon riuscirà se uno qualsiasi dei valori nell'array sono Funzioni o hanno prototipi (come a Date).
Dancrumb,

L'uso della giunzione è una soluzione parziale. Fallirà in molti più casi di JSON. Splice crea una copia profonda di stringhe e numeri, quando sposta valori - non ha mai detto che restituisce una copia.
tfmontague,

1
Perché giuntare (0)? Non dovrebbe essere slice ()? Penso che non si debba modificare l'array originale, cosa che fa la giunzione. @JamesMontagne
helpse

2
splice creerà puntatori agli elementi dell'array originale (copia superficiale). splice (0) alloca la nuova memoria (copia profonda) per gli elementi dell'array che sono numeri o stringhe e crea puntatori per tutti gli altri tipi di elementi (copia superficiale). Passando un valore iniziale pari a zero al metodo della funzione di giunzione, non giunge alcun elemento dell'array originale e quindi non lo modifica.
tfmontague,

1
In realtà, esiste un solo tipo di array: un array di "qualcosa". Non vi è alcuna differenza tra [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]e qualsiasi altro array.
wizzwizz4,

189

È possibile utilizzare gli spread di array ...per copiare array.

const itemsCopy = [...items];

Inoltre, se vuoi creare un nuovo array con quello esistente che ne fa parte:

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

Gli spread dell'array sono ora supportati in tutti i principali browser, ma se hai bisogno di supporto più vecchio usa dattiloscritto o babel e compila su ES5.

Maggiori informazioni sugli spread


1
Questo non funzionerà per la copia profonda. Deep Clone an Array in JavaScript .
SNag

151

Non è necessario jQuery ... Esempio di lavoro

var arr2 = arr1.slice()

Questo copia l'array dalla posizione iniziale 0fino alla fine dell'array.

È importante notare che funzionerà come previsto per i tipi primitivi (stringa, numero, ecc.) E spiegare anche il comportamento previsto per i tipi di riferimento ...

Se si dispone di una matrice di tipi di riferimento, dire di tipo Object. L'array verrà copiato, ma entrambi gli array conterranno riferimenti agli stessi Object. Quindi in questo caso sembrerebbe che l'array sia copiato per riferimento anche se l'array è effettivamente copiato.


12
No, questa non sarebbe una copia profonda.
jondavidjohn,

Prova questo; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh,

2
Qual è la differenza tra questa risposta e la risposta accettata?
Isaac Pak,

ottenere un errore nella console per il tuo esempio "TypeError: window.addEvent non è una funzione"
Ravi Sharma

72

Un'alternativa a sliceè concat, che può essere utilizzata in 2 modi. Il primo di questi è forse più leggibile in quanto il comportamento previsto è molto chiaro:

var array2 = [].concat(array1);

Il secondo metodo è:

var array2 = array1.concat();

Cohen (nei commenti) ha sottolineato che quest'ultimo metodo ha prestazioni migliori .

Il modo in cui funziona è che il concatmetodo crea un nuovo array costituito dagli elementi nell'oggetto su cui viene chiamato seguito dagli elementi di qualsiasi array passatogli come argomenti. Quindi, quando non vengono passati argomenti, copia semplicemente l'array.

Lee Penkman, anche nei commenti, fa notare che se c'è una possibilità array1è undefined, è possibile restituire un array vuoto come segue:

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

Oppure, per il secondo metodo:

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

Si noti che è anche possibile fare questo con slice: var array2 = (array1 || []).slice();.


31
In realtà puoi anche fare: var array2 = array1.concat (); È molto più veloce per quanto riguarda le prestazioni. (JSPerf: jsperf.com/copy-simple-array e jsperf.com/copy-array-slice-vs-concat/5
Cohen

5
Vale la pena notare che se array1 non è un array, quindi [].concat(array1)restituisce [array1]ad esempio se è indefinito [undefined]. A volte lo facciovar array2 = [].concat(array1 || []);
Lee Penkman,

60

Ecco come l'ho fatto dopo aver provato molti approcci:

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

Questo creerà una nuova copia profonda non correlata alla prima (non una copia superficiale).

Anche questo ovviamente non clonerà eventi e funzioni, ma la cosa buona è che puoi farlo in una riga, e può essere usato per qualsiasi tipo di oggetto (array, stringhe, numeri, oggetti ...)


4
Questo è il migliore. Uso lo stesso metodo molto tempo fa e penso che non ci sia più senso nei cicli ricorsivi della vecchia scuola
Vladimir Kharlampidi,

1
Tenere presente che questa opzione non gestisce bene strutture simili a grafici: si arresta in modo anomalo in presenza di cicli e non conserva riferimenti condivisi.
Ruben,

1
Ciò fallisce anche per cose come Date, o addirittura, qualsiasi cosa abbia un prototipo. Inoltre, undefineds viene convertito in nulls.
Dancrumb,

7
Nessuno è abbastanza coraggioso da commentare la grave inefficienza della CPU e della memoria di serializzare il testo e quindi analizzare nuovamente un oggetto?
Lawrence Dol,

3
Questa soluzione è l'unica che ha funzionato. L'uso di slice () è davvero una soluzione falsa.

20

Alcuni dei metodi citati funzionano bene quando si lavora con tipi di dati semplici come numero o stringa, ma quando l'array contiene altri oggetti questi metodi falliscono. Quando proviamo a passare qualsiasi oggetto da un array a un altro, viene passato come riferimento, non come oggetto.

Aggiungi il seguente codice nel tuo file 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 semplicemente usa

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

Funzionerà.


2
viene visualizzato questo errore quando aggiungo questo codice alla mia pagina "Errore intervallo non rilevato: dimensione massima dello stack di chiamate superata"
nota il

1
Mi scuso, questo errore si verifica in Chrome se arr1 non viene dichiarato. quindi ho incollato il codice sopra riportato e ottengo l'errore, tuttavia, se dichiaro l'array arr1, non ottengo l'errore. Potresti migliorare la risposta dichiarando arr1 appena sopra arr2, vedo che ci sono alcuni 'noi' là fuori che non hanno riconosciuto che dovevamo dichiarare arr1 (in parte perché quando stavo valutando la tua risposta, ero di fretta e aveva bisogno di qualcosa che "
funzioni

.slice()funziona ancora bene anche se hai oggetti nel tuo array: jsfiddle.net/edelman/k525g
Jason

7
@Jason ma gli oggetti puntano ancora allo stesso oggetto, quindi cambiando l'uno cambierà l'altro. jsfiddle.net/k525g/1
Samuel

Codice eccellente. Una domanda che ho, in realtà ho provato a copiare un array in un altro come questo var arr1 = new Array () e quindi var arr2 = arr1; Se cambio qualcosa in arr2, la modifica avviene anche in arr1. Tuttavia, se uso il prototipo di clone che hai creato, in realtà crea una nuova istanza completa di quell'array o in altre parole lo copia. Quindi questo è un problema con il browser? o javascript per impostazione predefinita imposta le due variabili una puntando l'altra con l'uso di puntatori quando qualcuno fa var arr2 = arr1 e perché non accade con variabili intere? vedi jsfiddle.net/themhz/HbhtA
themhz l'


17

Personalmente penso che Array.from sia una soluzione più leggibile. A proposito, fai attenzione al supporto del suo browser.

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

1
Sì, questo è molto leggibile. La .slice()soluzione è del tutto non intuitiva. Grazie per questo.
Banago,

15

Importante!

La maggior parte delle risposte qui funziona per casi particolari .

Se non ti interessa l'uso di oggetti / oggetti profondi / nidificati ( ES6 ):

let clonedArray = [...array]

ma se vuoi fare un clone profondo usa questo invece:

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


Per gli utenti lodash:

let clonedArray = _.clone(array) documentazione

e

let clonedArray = _.cloneDeep(array) documentazione


11

Se ti trovi in ​​un ambiente di ECMAScript 6 , utilizzando Spread Operator puoi farlo in questo modo:

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>


9

Aggiunta alla soluzione di array.slice (); tenere presente che se si dispone di array secondari multidimensionali verranno copiati dai riferimenti. Quello che puoi fare è fare un ciclo e tagliare () ogni sotto-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]);

le stesse cose vanno alla matrice di oggetti, saranno copiati per riferimento, devi copiarli manualmente


Questa risposta merita un posto vicino alla parte superiore della pagina! Stavo lavorando con sotto array multidimensionali e non riuscivo a seguire il motivo per cui gli array interni venivano sempre copiati da ref e non da val. Questa semplice logica ha risolto il mio problema. Ti darei +100 se possibile!
Mac

8

I valori primitivi vengono sempre passati dal suo valore (copiato). I valori composti tuttavia vengono passati per riferimento.

Quindi, come possiamo copiare questo arr?

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

Copia un array in ES6

let arrCopy = [...arr]; 

Copia n Array in ES5

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

Perché `let arrCopy = arr` non passa per valore?

Il passaggio da una variabile a un'altra su valori composti come Oggetto / Matrice si comporta in modo diverso. Usando l'operatore asign sui valori di copand passiamo il riferimento a un oggetto. Questo è il motivo per cui il valore di entrambi gli array cambia durante la rimozione / aggiunta di elementi arr.

eccezioni:

arrCopy[1] = 'adding new value this way will unreference';

Quando si assegna un nuovo valore alla variabile, si modifica il riferimento stesso e non influisce sull'oggetto / matrice originale.

leggi di più


6

Come sappiamo in matrici e oggetti Javascript sono di riferimento, ma quali modi possiamo fare per copiare l'array senza cambiare l'array originale in un secondo momento?

Ecco alcuni modi per farlo:

Immagina di avere questo array nel tuo codice:

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

1) Effettuare il looping dell'array in una funzione e restituire un nuovo array, in questo modo:

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

2) Usando il metodo slice, slice è per tagliare parte dell'array, taglierà parte dell'array senza toccare l'originale, nella sezione, se non specifichi l'inizio e la fine dell'array, taglierà l'intero array e fondamentalmente facciamo una copia completa dell'array, quindi possiamo facilmente dire:

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

3) Anche metodo di contatto, questo è per unire due array, ma possiamo semplicemente specificare uno degli array e quindi fondamentalmente fare una copia dei valori nel nuovo array contattato:

var arr2 = arr.concat();

4) Ricalca e analizza anche il metodo, non è raccomandato, ma può essere un modo semplice per copiare Array e Oggetti:

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

5) Metodo Array.from, questo non è ampiamente supportato, prima dell'uso controlla il supporto in diversi browser:

const arr2 = Array.from(arr);

6) Modo ECMA6, anche non completamente supportato, ma babelJs può aiutarti se vuoi traspilare:

const arr2 = [...arr];

6
let a = [1,2,3];

Ora è possibile eseguire una delle seguenti operazioni per creare una copia di un array.

let b = Array.from(a); 

O

let b = [...a];

O

let b = new Array(...a); 

O

let b = a.slice(); 

O

let b = a.map(e => e);

Ora, se cambio a,

a.push(5); 

Quindi, a è [1,2,3,5] ma b è ancora [1,2,3] in quanto ha un riferimento diverso.

Ma penso che in tutti i metodi sopra Array.from sia migliore e fatto principalmente per copiare un array.


1
qual è il più veloce?
Marc Frame,


4

Nel mio caso particolare, dovevo assicurarmi che l'array rimanesse intatto, quindi per me ha funzionato

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

2
+1 per non aggiungere oscurità al tuo codice chiamando una funzione che fa esattamente la stessa cosa, ma in un modo meno ovvio. slice potrebbe essere più efficiente sotto il cofano, ma per chiunque lavori sul codice, questo dimostra il tuo intento. inoltre semplifica l'ottimizzazione in un secondo momento, se si desidera (ad esempio) filtrare ciò che si sta copiando. si noti tuttavia che ciò non gestisce la copia approfondita e gli stessi oggetti interni vengono passati al nuovo array, per riferimento. Questo potrebbe essere quello che vuoi fare, potrebbe non esserlo.
non sincronizzato

4

Crea una copia di array / oggetti multidimensionali:

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

Grazie a James Padolsey per questa funzione.

Fonte: qui


3

Dan, non c'è bisogno di usare trucchi fantasiosi. Tutto ciò che devi fare è fare una copia di arr1 facendo questo.

var arr2 = new Array(arr1);

Ora arr1e arr2sono due diverse variabili di array memorizzate in stack separati. Dai un'occhiata a jsfiddle .


Questo non copia l'array. Crea una matrice con un elemento che fa riferimento all'originale (ad es var arr2 = [arr1];.).
Timothy003,

3

Quando vogliamo copiare un array usando l'operatore di assegnazione ( =) non ne crea una copia, ma semplicemente copia il puntatore / riferimento all'array. Per esempio:

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

Spesso quando trasformiamo i dati vogliamo mantenere intatta la nostra struttura dati iniziale (ad es. Array). Facciamo questo facendo una copia esatta del nostro array in modo che questo possa essere trasformato mentre quello iniziale rimane intatto.

Modi per copiare un array:

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

Fai attenzione quando gli array o gli oggetti sono nidificati !:

Quando le matrici sono nidificate, i valori vengono copiati per riferimento. Ecco un esempio di come ciò potrebbe causare problemi:

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

Quindi non usare questi metodi quando ci sono oggetti o array all'interno dell'array che vuoi copiare. vale a dire, utilizzare questi metodi solo su array di primitivi.

Se vuoi effettuare la deepclone di un array javascript da utilizzare JSON.parseinsieme a JSON.stringify, in questo modo:

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

Esecuzione della copia:

Quindi quale scegliamo per prestazioni ottimali. Si scopre che il metodo più dettagliato, il forciclo ha le massime prestazioni. Usa ilfor loop per una copia veramente intensiva della CPU (grandi / molti array).

Successivamente il .slice()metodo ha anche prestazioni decenti ed è anche meno dettagliato e più facile da implementare per il programmatore. Suggerisco di utilizzare .slice()per la copia quotidiana di array che non richiedono molta CPU. Evita anche di usare ilJSON.parse(JSON.stringify(arr)) (un sacco di overhead) se non è richiesto un clone profondo e le prestazioni sono un problema.

Test delle prestazioni della fonte


3

Se l'array contiene elementi del tipo di dati primitivo come int, char o string ecc., È possibile utilizzare uno di quei metodi che restituiscono una copia dell'array originale come .slice () o .map () o l'operatore spread ( grazie a ES6).

new_array = old_array.slice()

o

new_array = old_array.map((elem) => elem)

o

const new_array = new Array(...old_array);

MA se la tua matrice contiene elementi complessi come oggetti (o matrici) o più oggetti nidificati , dovrai assicurarti di fare una copia di tutti gli elementi dal livello superiore all'ultimo livello, altrimenti il ​​riferimento interno gli oggetti verranno utilizzati e ciò significa che la modifica dei valori in object_elements in new_array influirà comunque su old_array. È possibile chiamare questo metodo di copia ad ogni livello come effettuare una COPIA PROFONDA PROFONDA di old_array.

Per la copia approfondita, è possibile utilizzare i metodi sopra menzionati per i tipi di dati primitivi a ciascun livello a seconda del tipo di dati oppure è possibile utilizzare questo metodo costoso (menzionato di seguito) per effettuare una copia profonda senza fare molto lavoro.

var new_array = JSON.parse(JSON.stringify(old_array));

Ci sono molti altri metodi là fuori che puoi usare a seconda delle tue esigenze. Ho citato solo alcuni di quelli per aver dato un'idea generale di ciò che accade quando proviamo a copiare un array nell'altro in base al valore .


Grazie mille, la tua risposta è stata l'unica che ha funzionato per il mio scenario,
albert sh

2

Se si desidera creare una nuova copia di un oggetto o di un array, è necessario copiare esplicitamente le proprietà dell'oggetto o degli elementi dell'array, ad esempio:

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

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

Puoi cercare ulteriori informazioni su Google su valori primitivi immutabili e riferimenti a oggetti mutabili.


1
Non è necessario copiare esplicitamente le proprietà degli oggetti dell'array. Vedi la risposta di Chtiwi Malek.
Magne,

2

L'utilizzo della copia profonda di jQuery potrebbe essere effettuato come segue:

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

2

È inoltre possibile utilizzare l'operatore di diffusione ES6 per copiare la matrice

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

2

Ecco alcuni altri modi per copiare:

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



2

Esempi rapidi:

  1. Se gli elementi dell'array sono tipi primitivi (stringa, numero, ecc.)

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. Se gli elementi dell'array sono letterali di oggetti, un altro array ({}, [])

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. Soluzione per 2 : Deep Copy per elemento per elemento

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


1

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

non è una cattiva idea, anche se farei meglio a usare JSON stringify / parse invece di eval, e ancora un altro confronto jsPerf sarebbe buono da verificare, inoltre nota che toSourcenon è standard e non funzionerà in Chrome per esempio.
dmi3y

1

C'è il nuovo introdotto Array.from, ma sfortunatamente, al momento della stesura di questo articolo è supportato solo sulle recenti versioni di Firefox (32 e successive). Può essere semplicemente usato come segue:

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

Riferimento: qui

Oppure Array.prototype.mappuò essere utilizzato con una funzione di identità:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

Riferimento: qui


+1 per citare Array.from, che ora è supportato su tutti i principali browser tranne Internet Explorer ( fonte ). .
mgthomas99,

Essere consapevoli di Array.frommutare i dati, non fornisce copia profonda / clonazione profonda.
Sudhansu Choudhary,

1

Per array ES6 contenenti oggetti

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.