1 29/10/2026 9 min

Quando si parla di clonare un oggetto JavaScript, il punto non è solo “fare una copia”: bisogna capire quanto profonda deve essere quella copia e quali tipi di dati contiene l’oggetto originale. In JavaScript molti valori complessi sono passati per riferimento, quindi una semplice assegnazione non crea un duplicato indipendente. È qui che nascono bug sottili: modifichi la “copia” e ti accorgi che hai cambiato anche l’originale.

In pratica esistono tre scenari ricorrenti: copia superficiale, clonazione profonda e serializzazione/deserializzazione. La scelta giusta dipende dal tipo di oggetto, dalla presenza di array annidati, Date, Map, Set, funzioni e riferimenti circolari. Non c’è un metodo universale perfetto: ogni tecnica ha vantaggi, limiti e casi d’uso precisi.

Assegnazione, copia superficiale e riferimento: il comportamento di base

Partiamo dal caso più semplice. Se assegni un oggetto a una nuova variabile, non stai creando una copia: stai creando un nuovo riferimento allo stesso oggetto.

const original = { nome: 'Mario', profilo: { ruolo: 'admin' } };
const copia = original; copia.nome = 'Luca';
console.log(original.nome); // 'Luca'

Qui original e copia puntano allo stesso oggetto in memoria. Cambiare una proprietà tramite una variabile riflette il cambiamento nell’altra. Questo comportamento è normale e spesso utile, ma diventa un problema quando vuoi isolare uno stato da modifiche accidentali.

La copia superficiale duplica il primo livello dell’oggetto, ma lascia condivisi gli oggetti annidati. In altre parole: i campi primitivi vengono copiati, i riferimenti interni no.

const original = { nome: 'Mario', profilo: { ruolo: 'admin' } };
const copia = { ...original }; copia.nome = 'Luca';
copia.profilo.ruolo = 'editor'; console.log(original.nome); // 'Mario'
console.log(original.profilo.ruolo); // 'editor'

Il primo valore cambia in modo indipendente, il secondo no. Questo è il punto chiave da ricordare: spread e Object.assign() sono ottimi per copiare oggetti piatti, ma non risolvono la clonazione profonda.

Clonazione superficiale con spread e Object.assign()

Per oggetti semplici, la soluzione più leggibile è spesso lo spread operator:

const originale = { id: 1, nome: 'Anna', attivo: true };
const copia = { ...originale };

In alternativa puoi usare Object.assign():

const originale = { id: 1, nome: 'Anna', attivo: true };
const copia = Object.assign({}, originale);

Le due tecniche sono simili nel risultato pratico. Lo spread è in genere preferito perché è più leggibile e immediato. Object.assign() resta utile quando devi unire più sorgenti in un solo oggetto, per esempio:

const base = { tema: 'chiaro', lingua: 'it' };
const override = { tema: 'scuro' };
const config = Object.assign({}, base, override);

Attenzione però: se l’oggetto contiene array o sotto-oggetti, la copia rimane superficiale. Questo significa che la copia è affidabile solo quando il dato è piatto o quando sai esattamente quali campi possono essere condivisi.

Clonazione profonda: quando serve davvero

La clonazione profonda crea una nuova struttura indipendente, compresi gli oggetti annidati. È la scelta giusta quando devi manipolare dati complessi senza alterare l’originale, ad esempio in stati applicativi, normalizzazione di payload, form editing o trasformazioni di dati interni.

Un esempio classico è un oggetto con array annidati:

const originale = { utente: 'Marta', preferenze: { notifiche: true, temi: ['dark', 'high-contrast'] }
};

Se fai una copia superficiale e modifichi preferenze.temi, il riferimento resta condiviso. Per evitare questo problema hai diverse strade.

structuredClone(): la soluzione moderna per molti casi reali

La funzione structuredClone() è oggi una delle opzioni più interessanti per clonare oggetti complessi in JavaScript moderno. È pensata per copiare strutture dati compatibili con lo structured clone algorithm, quindi gestisce bene molti casi pratici senza dover scrivere codice ricorsivo a mano.

const originale = { nome: 'Marta', preferenze: { notifiche: true, temi: ['dark', 'high-contrast'] }
}; const copia = structuredClone(originale); copia.preferenze.temi.push('light'); console.log(originale.preferenze.temi); // ['dark', 'high-contrast']
console.log(copia.preferenze.temi); // ['dark', 'high-contrast', 'light']

Il vantaggio è evidente: ottieni un clone profondo senza dover inventare una funzione custom per ogni progetto. Inoltre structuredClone() supporta diversi tipi nativi, come Date, Map, Set e oggetti annidati complessi, meglio di tante soluzioni artigianali basate su JSON.

Ma non è una bacchetta magica. Non clona tutto: le funzioni non sono clonabili, e alcuni oggetti speciali possono comportarsi in modo diverso rispetto a quanto ci si aspetta. Se nel tuo oggetto ci sono metodi, classi custom o riferimenti non serializzabili, va testato con attenzione.

JSON.parse(JSON.stringify()): utile, ma con limiti netti

Per anni una scorciatoia molto usata è stata questa:

const copia = JSON.parse(JSON.stringify(originale));

Funziona in tanti casi semplici e produce una vera copia separata per strutture JSON-compatible. Però il prezzo da pagare è alto: perdi informazioni importanti. Per esempio:

  • Date diventa una stringa

  • undefined viene eliminato

  • Infinity, -Infinity e NaN non sono preservati correttamente

  • funzioni, simboli e riferimenti circolari non sono gestiti

In altre parole, questa tecnica è accettabile solo se sai di avere dati puramente serializzabili in JSON e ti va bene accettarne i limiti. Per oggetti di business più ricchi, è una soluzione troppo fragile.

Confronto rapido tra i metodi più usati

Se devi scegliere in fretta, questa è una sintesi utile:

  • Spread / Object.assign(): ottimi per oggetti piatti e copie superficiali.

  • structuredClone(): preferibile per clonazioni profonde moderne e dati compatibili.

  • JSON.parse(JSON.stringify()): solo per casi semplici, con dati JSON-safe.

  • Clone manuale ricorsivo: utile quando servono regole su misura, ma più facile da sbagliare.

La scelta non dipende solo dalla comodità, ma anche dalla semantica dei dati. Se il tuo oggetto rappresenta stato UI, configurazioni o payload da elaborare, spesso vuoi una copia profonda. Se invece stai facendo merge di proprietà semplici, una copia superficiale può bastare e risultare più leggibile.

Esempio pratico: clonare un oggetto prima di modificarlo

Immagina un oggetto che rappresenta i dati di un utente in una schermata di modifica. Vuoi permettere all’utente di cambiare i campi senza toccare subito il record originale.

const utente = { nome: 'Elena', email: 'elena@example.com', impostazioni: { newsletter: true, tema: 'scuro' }
}; const form = structuredClone(utente);
form.nome = 'Elena Rossi';
form.impostazioni.tema = 'chiaro'; console.log(utente.nome); // 'Elena'
console.log(utente.impostazioni.tema); // 'scuro'

Questo è un caso reale molto comune. Se usassi una copia superficiale, la modifica a impostazioni.tema finirebbe anche nell’originale. Con structuredClone() invece isoli completamente la working copy.

Clonazione di array di oggetti

Il problema si complica quando hai un array di oggetti. Molti sviluppatori fanno una copia dell’array con [...array] e credono di aver risolto tutto. In realtà hanno clonato solo il contenitore, non gli oggetti interni.

const lista = [ { id: 1, nome: 'A' }, { id: 2, nome: 'B' }
]; const copia = [...lista];
copia[0].nome = 'Z'; console.log(lista[0].nome); // 'Z'

Se vuoi una copia indipendente anche degli elementi, devi clonare ogni oggetto interno:

const copiaProfonda = lista.map(item => ({ ...item }));

Questo va bene se ogni elemento è piatto. Se invece gli elementi contengono a loro volta oggetti annidati, serve una clonazione profonda vera e propria:

const copiaProfonda = structuredClone(lista);

Oggetti complessi: Date, Map, Set e classi

Uno dei motivi per cui la clonazione in JavaScript è così spesso sottovalutata è che i dati reali non sono quasi mai “semplici”. Un oggetto può includere Date, Map, Set, istanze di classi o campi derivati da librerie esterne.

Con JSON perdi gran parte di queste informazioni. Con spread e Object.assign() non risolvi il problema dei riferimenti annidati. structuredClone() copre meglio molti di questi casi, ma non sostituisce la comprensione della struttura dati: se un oggetto contiene metodi di classe o logiche di comportamento, forse non ti serve “clonare” in senso stretto, ma creare una nuova istanza con stato copiato in modo controllato.

Per esempio, se hai una classe così:

class Utente { constructor(nome) { this.nome = nome; } saluta() { return `Ciao ${this.nome}`; }
}

Clonare l’istanza come semplice oggetto plain può farti perdere il prototipo e quindi i metodi. In questi casi è spesso meglio progettare un costruttore o un metodo dedicato, ad esempio clone(), che ricrei correttamente il comportamento dell’oggetto.

Clone manuale: quando ha senso scriverlo a mano

Scrivere una funzione di clonazione manuale ha senso quando hai regole molto specifiche: vuoi preservare certi tipi, escludere campi, trasformare valori o gestire strutture non standard. Il problema è che il clone manuale richiede attenzione per evitare bug difficili da diagnosticare.

function cloneOggetto(obj) { if (obj === null || typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(cloneOggetto); } const copia = {}; for (const chiave in obj) { if (Object.prototype.hasOwnProperty.call(obj, chiave)) { copia[chiave] = cloneOggetto(obj[chiave]); } } return copia;
}

Questo esempio mostra l’idea base, ma non è una soluzione completa per ambienti reali. Non gestisce bene Date, Map, Set, riferimenti circolari, simboli o prototipi. Serve quindi solo come dimostrazione didattica o come punto di partenza per una funzione più robusta.

Riferimenti circolari: il caso che rompe molte soluzioni improvvisate

Un riferimento circolare si verifica quando un oggetto contiene, direttamente o indirettamente, un riferimento a sé stesso. È un caso meno raro di quanto sembri, soprattutto in strutture dati complesse o oggetti collegati tra loro.

const a = {};
a.self = a;

In questo scenario, JSON fallisce. Un clone manuale ingenuo può entrare in ricorsione infinita. structuredClone() invece è progettato per gestire queste strutture in modo corretto, ed è uno dei motivi principali per cui oggi è spesso la scelta migliore quando l’ambiente lo supporta.

Regola pratica per scegliere il metodo giusto

  1. Se l’oggetto è piatto e vuoi solo duplicarlo, usa { ...obj } o Object.assign().

  2. Se hai oggetti annidati e vuoi una copia indipendente, prova structuredClone().

  3. Se i dati sono compatibili con JSON e sai accettare i limiti, JSON.parse(JSON.stringify()) può bastare.

  4. Se hai classi, logiche custom o campi particolari, valuta un metodo dedicato invece di una clonazione generica.

Questa regola non è teorica: evita il classico errore di usare una tecnica “comoda” per un problema che richiede invece controllo preciso. La clonazione non è un dettaglio sintattico, ma una scelta di modellazione dei dati.

Verifica rapida: come controllare se hai davvero clonato

Quando testi una clonazione, non limitarti a controllare se i valori sembrano uguali. Devi verificare anche l’indipendenza dei riferimenti interni. Un controllo semplice consiste nel modificare un campo annidato nella copia e osservare se l’originale cambia.

const originale = { dati: { valore: 10 }
}; const copia = structuredClone(originale);
copia.dati.valore = 99; console.log(originale.dati.valore); // deve restare 10

Se il valore dell’originale cambia, non hai una vera copia indipendente. Questo test banale è spesso sufficiente per intercettare un bug prima che arrivi in produzione, soprattutto quando lavori su form, editor, carrelli o stati intermedi di applicazioni frontend e backend.

Conclusione tecnica: la copia giusta dipende dal dato, non dalla scorciatoia

Clonare un oggetto JavaScript non significa solo duplicare una sintassi: significa decidere se vuoi condividere o separare lo stato. Per oggetti semplici bastano spesso spread e Object.assign(). Per strutture annidate e dati reali, structuredClone() è la soluzione più pulita nella maggior parte dei casi moderni. JSON resta una scorciatoia utile, ma con limiti molto chiari. E quando il dato è davvero particolare, la strada migliore è progettare una clonazione esplicita, non affidarsi a un trucco generico.