Introduzione
Quando si lavora con gli Array in JavaScript, una delle difficoltà più comuni non riguarda la creazione o la lettura dei dati, ma il modo in cui questi vengono modificati. Metodi come push(), pop() e splice() cambiano direttamente l’array originale. Questo comportamento è chiamato mutazione.
La mutazione non è sempre un errore, ma può generare problemi quando lo stesso array viene utilizzato in più parti dell’applicazione. Un componente dell’interfaccia, una funzione di validazione e una funzione di calcolo potrebbero condividere lo stesso riferimento. Se una di queste funzioni modifica l’array, anche le altre vedranno dati diversi da quelli iniziali.
In questo tutorial vedremo come:
- creare array in modo chiaro;
- distinguere tra riferimento, copia superficiale e copia profonda;
- aggiornare array senza modificarli direttamente;
- usare metodi di iterazione per produrre nuovi risultati;
- gestire un piccolo elenco di attività in modo sicuro e prevedibile.
Codice completo
// Elenco iniziale delle attività
const attivita = [
{ id: 1, titolo: "Studiare JavaScript", completata: false },
{ id: 2, titolo: "Scrivere gli esercizi", completata: true },
{ id: 3, titolo: "Ripassare gli Array", completata: false }
];
// 1. Creiamo un nuovo array senza modificare quello originale
const nuovaAttivita = {
id: 4,
titolo: "Leggere la documentazione",
completata: false
};
const elencoAggiornato = [...attivita, nuovaAttivita];
console.log("Array originale:", attivita);
console.log("Array aggiornato:", elencoAggiornato);
// 2. Aggiorniamo una sola attività con map()
const elencoCompletato = elencoAggiornato.map(attivita => {
if (attivita.id === 1) {
return {
...attivita,
completata: true
};
}
return attivita;
});
// 3. Eliminiamo un elemento con filter()
const elencoSenzaSecondaAttivita = elencoCompletato.filter(
attivita => attivita.id !== 2
);
// 4. Cerchiamo un elemento con find()
const primaAttivitaNonCompletata = elencoSenzaSecondaAttivita.find(
attivita => !attivita.completata
);
// 5. Calcoliamo un riepilogo con reduce()
const riepilogo = elencoSenzaSecondaAttivita.reduce(
(risultato, attivita) => {
risultato.totale++;
if (attivita.completata) {
risultato.completate++;
} else {
risultato.daCompletare++;
}
return risultato;
},
{
totale: 0,
completate: 0,
daCompletare: 0
}
);
console.log("Dopo l´aggiornamento:", elencoCompletato);
console.log("Dopo la rimozione:", elencoSenzaSecondaAttivita);
console.log("Prima attività non completata:", primaAttivitaNonCompletata);
console.log("Riepilogo:", riepilogo); Spiegazione
Riferimenti e copie degli array
Un array assegnato a una nuova variabile non viene copiato automaticamente. Le due variabili fanno riferimento allo stesso oggetto in memoria:
const numeri = [1, 2, 3];
const altroRiferimento = numeri;
altroRiferimento.push(4);
console.log(numeri); // [1, 2, 3, 4] Per creare un nuovo array è possibile usare l’operatore spread:
const numeri = [1, 2, 3];
const copia = [...numeri];
copia.push(4);
console.log(numeri); // [1, 2, 3]
console.log(copia); // [1, 2, 3, 4] Questa tecnica crea una copia superficiale. Gli elementi primitivi, come numeri e stringhe, vengono copiati correttamente. Se però l’array contiene oggetti, gli oggetti interni mantengono il proprio riferimento.
const utenti = [
{ nome: "Anna", attivo: true }
];
const copiaUtenti = [...utenti];
copiaUtenti[0].attivo = false;
console.log(utenti[0].attivo); // false Per aggiornare un oggetto senza modificare quello originale, è necessario copiare anche l’oggetto interessato. Il metodo map() è ideale perché visita ogni elemento e restituisce un nuovo array.
const utentiAggiornati = utenti.map(utente => {
return {
...utente,
attivo: true
};
}); Aggiungere, aggiornare e rimuovere elementi
Per aggiungere un elemento alla fine senza usare push(), si può creare un nuovo array con spread:
const numeri = [10, 20];
const risultato = [...numeri, 30]; Per aggiungere un elemento all’inizio è sufficiente invertire l’ordine:
const risultato = [5, ...numeri]; Per rimuovere elementi senza splice(), si usa filter(). Il callback deve restituire true per conservare un elemento e false per eliminarlo dal nuovo array.
const prodotti = [
{ id: 1, nome: "Mouse" },
{ id: 2, nome: "Tastiera" }
];
const senzaMouse = prodotti.filter(prodotto => prodotto.id !== 1); Iterare per ottenere informazioni
Il metodo find() restituisce il primo elemento che soddisfa una condizione, oppure undefined se non trova risultati. È utile quando si cerca un singolo record.
Il metodo reduce(), invece, permette di trasformare molti elementi in un unico valore: un numero, una stringa, un oggetto o anche un altro array. Nell’esempio, viene creato un oggetto riepilogativo con il conteggio delle attività.
Rispetto a una serie di modifiche distribuite nel codice, questa tecnica rende il flusso dei dati più facile da controllare: ogni operazione riceve un array e restituisce un nuovo risultato.
Best practice
- Preferisci
const: impedisce di riassegnare la variabile, pur permettendo modifiche interne all’array. Per evitare anche le mutazioni, usa metodi che restituiscono nuovi array. - Usa spread per le copie semplici:
[...array]è leggibile e adatto ad array di valori primitivi. - Ricorda la copia superficiale: con array di oggetti, copia anche l’oggetto modificato usando
{ ...oggetto }. - Scegli il metodo in base all’obiettivo:
map()per trasformare,filter()per selezionare,find()per cercare un elemento ereduce()per aggregare. - Evita callback con effetti collaterali: una funzione passata a
map()dovrebbe produrre un nuovo valore, non modificare dati esterni. - Controlla i casi vuoti:
find()può restituireundefinedereduce()dovrebbe avere un valore iniziale esplicito. - Usa
structuredClone()con attenzione: può creare copie profonde di strutture compatibili, ma non è sempre necessario e non sostituisce una progettazione corretta dei dati.
Riepilogo
Gli array JavaScript sono strutture flessibili, ma la loro mutabilità richiede attenzione. Assegnare un array a una nuova variabile crea un riferimento, non una copia. Per produrre un nuovo array si può usare lo spread oppure un metodo di iterazione come map() o filter().
Un approccio immutabile consiste nel non modificare direttamente i dati esistenti: si crea invece una nuova versione dell’array. Questo stile è particolarmente utile nelle applicazioni web, nei sistemi con stato condiviso e nei test automatici, perché rende più prevedibili le trasformazioni.
La regola pratica è semplice: usa map() per aggiornare elementi, filter() per rimuoverli, spread per aggiungerli e reduce() per creare riepiloghi. Quando gli elementi sono oggetti, copia anche l’oggetto che stai modificando.
