Categoria:
Programmazione

Variabili: copie, riferimenti e cloni

Una delle fonti di bug che più frequentemente ci capita di incontrare quando sviluppiamo software per il web (gestionali per aziende, documentali web based, CMS, web app, ...), soprattutto quando lavoriamo sul codice di terze parti o sul codice prodotto internamente dai nostri Clienti è concetto di "copia vs riferimento".

Che tu sia un programmatore javascript, un project manager per un sito web o un product manager per una app, non puoi non sapere quel che ti stiamo per dire...

Copia vs Riferimento: Valori Primitivi

Partiamo dalle cose più semplici: copie e riferimenti di valori primitivi, con un esempio in JavaScript.

let azienda = 'Librasoft';

let azienda2 = azienda;

console.log(azienda); // Librasoft

console.log(azienda2); // Librasoft

azienda = 'Kuma';

console.log(azienda); // Kuma

console.log(azienda2); // Librasoft

Da questo esempio si evince che quando si vuole assegnare una variabile di valori primitivi (stringhe, ma anche interi, booleani, ...) a un'altra variabile, la seconda diventa una copia della prima. Ciò significa che i cambiamenti sulla prima variabile non si rispecchieranno sulla seconda.

Copia vs Riferimento: Array

Cosa succede quando invece si lavora con gli array? Vediamo un altro esempio.

let software = ['Jester', 'Doko', 'GeCo'];

let prodotti = software;

console.log(software); // Jester, Doko, GeCo

console.log(prodotti); // Jester, Doko, GeCo

software[0] = 'XXX';

console.log(software); // XXX, Doko, GeCo

console.log(prodotti); // XXX, Doko, GeCo

Da questo esempio vediamo come il cambio del primo array abbia modificato in qualche modo anche i contenuti del secondo array... È un errore?

No: è il funzionamento atteso (nonché la fonte di numerosi bug da parte di team inesperti), in quanto gli array non sono tipi primitivi, perciò quando si assegna una variabile che "contiene" un array a un'altra... non se ne passa la copia, bensì il riferimento.

Quindi, come fare per copiare un array su un altro? Una delle possibili risposte è nel seguente snippet.

let software = ['Jester', 'Doko', 'GeCo'];

let prodotti = Array.from(software);

console.log(software); // Jester, Doko, GeCo

console.log(prodotti); // Jester, Doko, GeCo

software[0] = 'XXX';

console.log(software); // XXX, Doko, GeCo

console.log(prodotti); // Jester, Doko, GeCo

Copia vs Riferimento: Oggetti

Anche gli oggetti, come gli array, vengono passati per riferimento. Vediamo un terzo esempio.

let software = {nome: 'Jester', tipo: 'Gestionale'};

let prodotto = software;

console.log(software); // nome: Jester, tipo: Software Gestionale

console.log(prodotto); // nome: Jester, tipo: Software Gestionale

software.tipo = 'Gestionale Aziendale';

console.log(software); // nome: Jester, tipo: Gestionale Aziendale

console.log(prodotto); // nome: Jester, tipo: Gestionale Aziendale

Anche in questo caso, le modifiche sul primo oggetto sembrano creare un effetto collaterale sul secondo oggetto... anche se, in realtà, è il funzionamento corretto del passaggio per riferimento.

Come fare a copiare un oggetto? Una possibile soluzione è nell'esempio che segue.

let software = {nome: 'Jester', tipo: 'Software Gestionale'};

let prodotto = Object.assign({}, software);

console.log(software); // nome: Jester, tipo: Software Gestionale

console.log(prodotto); // nome: Jester, tipo: Software Gestionale

software.tipo = 'Gestionale Aziendale';

console.log(software); // nome: Jester, tipo: Gestionale Aziendale

console.log(prodotto); // nome: Jester, tipo: Software Gestionale

Copia superficiale vs copia profonda

Un'altra fonte di bug che spesso ci capita di incontrare è dovuta alla differenza tra copia superficiale (shallow copy) e copia profonda (deep copy) di un oggetto JavaScript.

Vediamo il seguente esempio, dove vogliamo copiare un oggetto che contiene un altro oggetto.

let azienda = {nome: 'Librasoft', prodotto: {nome: 'Jester', tipo: 'Software'}};

let azienda2 = Object.assign({}, azienda);

console.log(azienda); // nome: Librasoft, prodotto: {nome: Jester, tipo: Software}

console.log(azienda); // nome: Librasoft, prodotto: {nome: Jester, tipo: Software}

azienda.nome = 'Kuma';

azienda.prodotto.nome = 'Consulenza';

azienda.prodotto.tipo = 'Servizio';

console.log(azienda); // nome: Kuma, prodotto: {nome: Consulenza, tipo: Servizio}

console.log(azienda2); // nome: Librasoft, prodotto: {nome: Consulenza, tipo: Servizio}

Dall'esempio si vede come le modifiche apportate all'oggetto "interno" della prima variabile si rispecchino anche nella seconda variabile. Questo accade perché il metodo "assign" esegue una copia di ciò che trova sulla superficie dell'oggetto (il suo primo livello), e non esegue la copia in maniera ricorsiva.

Per effettuare la copia profonda (o clone) è quindi necessario scrivere appositamente una funzione che esegua la deep copy, oppure utilizzare una libreria che lo faccia al posto nostro.

Hai bisogno di aiuto nello sviluppo del tuo software aziendale?

Se il software custom che attualmente utilizzi nella tua azienda presenta molti bug, o se pensi che al tuo team di sviluppo serva una mano a realizzare una particolare feature del tuo gestionale, portale o app... contattaci senza impegno: analizzeremo il tuo software, identificheremo le aree più critiche e ti prospetteremo la migliore soluzione.