Destructuring & Spread Operator
Sintassi ES6+ moderna per estrarre valori e manipolare array/oggetti
✨ Sintassi Moderna ES6+
Destructuring e Spread/Rest sono features ES6 (2015)
che rendono il codice più pulito, leggibile e conciso. Sono usate ovunque nel JavaScript moderno!
💡 Perché Sono Importanti?
- ✅ Codice più pulito – Meno righe, più leggibilità
- ✅ Estrazione facile – Prendi valori da array/oggetti
- ✅ Parametri funzioni – Più flessibili e chiari
- ✅ Standard moderno – Usate in React, Vue, Node.js…
- ✅ Array Destructuring – Estrai valori da array
- ✅ Object Destructuring – Estrai proprietà da oggetti
- ✅ Spread Operator (…) – Espandi array/oggetti
- ✅ Rest Parameters – Raccogli argomenti
- ✅ Casi d’Uso Pratici – Esempi reali
📦 Array Destructuring
Estrai valori da un array assegnandoli direttamente a variabili.
const numeri = [1, 2, 3, 4, 5]; // ❌ VECCHIO MODO const primo = numeri[0]; const secondo = numeri[1]; const terzo = numeri[2]; // ✅ CON DESTRUCTURING (moderno) const [a, b, c] = numeri; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3 // Salta elementi const [x, , z] = numeri; // Salta il secondo console.log(x); // 1 console.log(z); // 3 // Primi N elementi const [primo, secondo] = numeri; console.log(primo); // 1 console.log(secondo); // 2 // Con valori di default const arr = [10]; const [val1, val2 = 20] = arr; console.log(val1); // 10 console.log(val2); // 20 (default)
Raccogli i rimanenti elementi in un array.
const numeri = [1, 2, 3, 4, 5, 6, 7];
// Primo e resto
const [primo, ...resto] = numeri;
console.log(primo); // 1
console.log(resto); // [2, 3, 4, 5, 6, 7]
// Primi due e resto
const [a, b, ...altri] = numeri;
console.log(a); // 1
console.log(b); // 2
console.log(altri); // [3, 4, 5, 6, 7]
// ⚠️ Rest deve essere l'ultimo elemento!
// const [...resto, ultimo] = numeri; // ERRORE!
// Esempio pratico: primi e ultimi
const [first, second, ...middle] = numeri;
const last = numeri[numeri.length - 1];
console.log(`Primo: ${first}`);
console.log(`Secondo: ${second}`);
console.log(`Ultimi: ${middle}`);
console.log(`Last: ${last}`);
Scambia valori senza variabile temporanea!
let a = 1; let b = 2; // ❌ VECCHIO MODO let temp = a; a = b; b = temp; // ✅ CON DESTRUCTURING (elegante!) [a, b] = [b, a]; console.log(a); // 2 console.log(b); // 1 // Esempio: ordinamento let x = 10, y = 5, z = 8; if (x < y) [x, y] = [y, x]; if (y < z) [y, z] = [z, y]; if (x < y) [x, y] = [y, x]; console.log(x, y, z); // 10, 8, 5 (ordinato desc)
// Funzione che restituisce array
function getCoordinate() {
return [10, 20];
}
// Destructuring del return
const [x, y] = getCoordinate();
console.log(x, y); // 10, 20
// Esempio: split
const fullName = "Mario Rossi";
const [nome, cognome] = fullName.split(" ");
console.log(nome); // "Mario"
console.log(cognome); // "Rossi"
// Esempio: Object.entries()
const persona = { nome: "Anna", età: 25 };
for (const [chiave, valore] of Object.entries(persona)) {
console.log(`${chiave}: ${valore}`);
}
// nome: Anna
// età: 25
// Esempio: useState React
// const [count, setCount] = useState(0); // Pattern comune!
🎯 Object Destructuring
Estrai proprietà da un oggetto assegnandole a variabili.
Molto usato per parametri funzioni e API!
const utente = {
nome: "Mario",
età: 30,
email: "mario@example.com",
città: "Roma"
};
// ❌ VECCHIO MODO
const nome = utente.nome;
const età = utente.età;
const email = utente.email;
// ✅ CON DESTRUCTURING (moderno)
const { nome, età, email } = utente;
console.log(nome); // "Mario"
console.log(età); // 30
console.log(email); // "mario@example.com"
// Non serve estrarre tutto
const { nome, città } = utente;
// Con valori di default
const { nome, paese = "Italia" } = utente;
console.log(paese); // "Italia" (default, non esiste in oggetto)
// Rinominare variabili
const { nome: firstName, età: age } = utente;
console.log(firstName); // "Mario"
console.log(age); // 30
Estrai da oggetti dentro oggetti.
const utente = {
nome: "Mario",
indirizzo: {
via: "Via Roma 10",
città: "Milano",
cap: "20100"
},
contatti: {
email: "mario@example.com",
telefono: "123456789"
}
};
// Destructuring annidato
const {
nome,
indirizzo: { città, cap },
contatti: { email }
} = utente;
console.log(nome); // "Mario"
console.log(città); // "Milano"
console.log(cap); // "20100"
console.log(email); // "mario@example.com"
// ⚠️ 'indirizzo' e 'contatti' NON sono variabili!
// console.log(indirizzo); // ERRORE!
// Se vuoi anche il parent:
const {
indirizzo,
indirizzo: { città }
} = utente;
console.log(indirizzo); // Oggetto completo
console.log(città); // "Milano"
Pattern molto comune e utile! ⭐
// ❌ VECCHIO MODO
function creaUtente(nome, età, email, città) {
return {
nome: nome,
età: età,
email: email,
città: città
};
}
creaUtente("Mario", 30, "mario@example.com", "Roma");
// ✅ CON DESTRUCTURING (più chiaro!)
function mostraUtente({ nome, età, email }) {
console.log(`${nome}, ${età} anni`);
console.log(`Email: ${email}`);
}
const utente = {
nome: "Anna",
età: 25,
email: "anna@example.com",
città: "Milano" // Non estratta, ignorata
};
mostraUtente(utente);
// Con valori di default
function creaPost({ titolo, contenuto, autore = "Anonimo" }) {
return {
titolo,
contenuto,
autore,
data: new Date()
};
}
const post = creaPost({
titolo: "Hello World",
contenuto: "Primo post"
});
console.log(post.autore); // "Anonimo" (default)
// Esempio API/Config
function configuraApp({
apiUrl = "https://api.example.com",
timeout = 5000,
retries = 3,
debug = false
} = {}) { // = {} permette chiamata senza argomenti
console.log("Config:", { apiUrl, timeout, retries, debug });
}
configuraApp({ apiUrl: "https://custom.api", debug: true });
configuraApp(); // Usa tutti i default
const utente = {
id: 1,
nome: "Mario",
età: 30,
email: "mario@example.com",
città: "Roma",
paese: "Italia"
};
// Estrai alcune proprietà, raccogli il resto
const { nome, età, ...resto } = utente;
console.log(nome); // "Mario"
console.log(età); // 30
console.log(resto);
// { id: 1, email: "mario@example.com", città: "Roma", paese: "Italia" }
// Esempio pratico: rimuovi proprietà
const { password, ...utentePublico } = {
id: 1,
nome: "Mario",
email: "mario@example.com",
password: "secret123"
};
console.log(utentePublico);
// { id: 1, nome: "Mario", email: "mario@example.com" }
// Password rimossa! ✅
// Esempio: separare props React
function Button({ onClick, className, ...restProps }) {
return {
onClick,
className,
altri: restProps
};
}
🌟 Spread Operator (...)
Lo spread operator "espande" array o oggetti.
Opposto del rest, ma stessa sintassi ...
const numeri = [1, 2, 3]; // Espandi array console.log(...numeri); // 1 2 3 (tre valori separati) // Copia array (shallow copy) const copia = [...numeri]; copia.push(4); console.log(numeri); // [1, 2, 3] (originale invariato) console.log(copia); // [1, 2, 3, 4] // Concatena array const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; // ❌ VECCHIO MODO const merged1 = arr1.concat(arr2); // ✅ CON SPREAD (moderno) const merged2 = [...arr1, ...arr2]; console.log(merged2); // [1, 2, 3, 4, 5, 6] // Aggiungi elementi const arr = [2, 3, 4]; const extended = [1, ...arr, 5, 6]; console.log(extended); // [1, 2, 3, 4, 5, 6] // Converti string in array const str = "Ciao"; const chars = [...str]; console.log(chars); // ["C", "i", "a", "o"] // Converti Set/Map in array const set = new Set([1, 2, 3, 3, 4]); const arrFromSet = [...set]; console.log(arrFromSet); // [1, 2, 3, 4]
const utente = {
nome: "Mario",
età: 30
};
// Copia oggetto (shallow copy)
const copia = { ...utente };
copia.nome = "Luigi";
console.log(utente.nome); // "Mario" (originale invariato)
console.log(copia.nome); // "Luigi"
// Merge oggetti
const base = { nome: "Mario", età: 30 };
const extra = { email: "mario@example.com", città: "Roma" };
const completo = { ...base, ...extra };
console.log(completo);
// { nome: "Mario", età: 30, email: "mario@example.com", città: "Roma" }
// Sovrascrivere proprietà
const originale = { nome: "Mario", età: 30 };
const aggiornato = { ...originale, età: 31 };
console.log(aggiornato);
// { nome: "Mario", età: 31 }
// L'ultimo vince
const obj = { ...{ a: 1 }, ...{ a: 2 } };
console.log(obj); // { a: 2 }
// Aggiungere proprietà
const utente = { nome: "Mario" };
const conId = { id: 1, ...utente, attivo: true };
console.log(conId);
// { id: 1, nome: "Mario", attivo: true }
// Esempio pratico: update immutabile
const state = { count: 0, user: "Mario" };
const newState = { ...state, count: state.count + 1 };
console.log(state.count); // 0 (invariato)
console.log(newState.count); // 1
// Math.max() vuole argomenti separati
const numeri = [5, 2, 9, 1, 7];
// ❌ VECCHIO MODO
const max1 = Math.max.apply(null, numeri);
// ✅ CON SPREAD (moderno)
const max2 = Math.max(...numeri);
console.log(max2); // 9
// Anche Math.min
const min = Math.min(...numeri);
console.log(min); // 1
// Array.push() multiplo
const arr = [1, 2, 3];
const toAdd = [4, 5, 6];
// ❌ MALE
arr.push(toAdd); // [[4, 5, 6]]
// ✅ BENE
arr.push(...toAdd);
console.log(arr); // [1, 2, 3, 4, 5, 6]
// Funzioni custom
function somma(a, b, c) {
return a + b + c;
}
const valori = [1, 2, 3];
console.log(somma(...valori)); // 6
🔄 Rest vs Spread
Stessa sintassi ..., ma comportamento opposto!
| Aspetto | Rest (...) | Spread (...) |
|---|---|---|
| Scopo | Raccoglie elementi | Espande elementi |
| Dove si usa | Lato sinistro (=) | Lato destro (=) |
| Risultato | Array o Oggetto | Elementi separati |
| Contesto | Destructuring, parametri | Chiamate, letterali |
// REST - Raccoglie (lato sinistro)
const [first, ...rest] = [1, 2, 3, 4];
// first = 1
// rest = [2, 3, 4] ← Array
function somma(...numeri) {
// numeri = [1, 2, 3, 4] ← Array
return numeri.reduce((acc, n) => acc + n, 0);
}
// SPREAD - Espande (lato destro)
const arr = [1, 2, 3];
const expanded = [...arr, 4, 5];
// expanded = [1, 2, 3, 4, 5] ← Nuovo array
console.log(...arr); // 1 2 3 ← Valori separati
// Insieme
function analizza(a, b, ...resto) { // REST nei parametri
const tutti = [a, b, ...resto]; // SPREAD in array
return tutti;
}
console.log(analizza(1, 2, 3, 4, 5)); // [1, 2, 3, 4, 5]
💼 Casi d'Uso Pratici
// Risposta API tipica
const response = {
data: {
id: 1,
nome: "Mario",
email: "mario@example.com",
profile: {
avatar: "avatar.jpg",
bio: "Developer"
}
},
meta: {
status: 200,
timestamp: "2025-01-02"
}
};
// Estrai solo ciò che serve
const {
data: { nome, email, profile: { avatar } },
meta: { status }
} = response;
console.log(nome, email, avatar, status);
// Fetch con destructuring
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
const { data, error } = await response.json();
if (error) {
console.error(error);
return null;
}
return data;
}
// Hook personalizzato (stile React)
function useAPI(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// ...
return { data, loading, error };
}
// Uso
const { data, loading, error } = useAPI('/api/users');
Pattern fondamentale in React, Redux, ecc.
// State iniziale
const state = {
user: { nome: "Mario", età: 30 },
tasks: [
{ id: 1, title: "Task 1", done: false },
{ id: 2, title: "Task 2", done: true }
],
settings: { theme: "light", lang: "it" }
};
// ✅ Update user (immutabile)
const newState1 = {
...state,
user: { ...state.user, età: 31 }
};
// ✅ Add task (immutabile)
const newTask = { id: 3, title: "Task 3", done: false };
const newState2 = {
...state,
tasks: [...state.tasks, newTask]
};
// ✅ Update task (immutabile)
const newState3 = {
...state,
tasks: state.tasks.map(task =>
task.id === 1
? { ...task, done: true }
: task
)
};
// ✅ Remove task (immutabile)
const newState4 = {
...state,
tasks: state.tasks.filter(task => task.id !== 1)
};
// ✅ Merge settings (immutabile)
const newState5 = {
...state,
settings: { ...state.settings, theme: "dark" }
};
console.log(state); // Originale invariato sempre! ✅
// Rimuovi proprietà
function omit(obj, ...keys) {
const copy = { ...obj };
keys.forEach(key => delete copy[key]);
return copy;
}
const user = { id: 1, nome: "Mario", password: "secret", email: "m@ex.com" };
const safe = omit(user, "password", "id");
console.log(safe); // { nome: "Mario", email: "m@ex.com" }
// Pick proprietà
function pick(obj, ...keys) {
return keys.reduce((acc, key) => {
if (key in obj) acc[key] = obj[key];
return acc;
}, {});
}
const picked = pick(user, "nome", "email");
console.log(picked); // { nome: "Mario", email: "m@ex.com" }
// Merge deep (semplice)
function merge(target, source) {
return {
...target,
...source,
// Merge nested objects
...Object.keys(source).reduce((acc, key) => {
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
acc[key] = merge(target[key] || {}, source[key]);
}
return acc;
}, {})
};
}
// Remove duplicates
function unique(arr) {
return [...new Set(arr)];
}
console.log(unique([1, 2, 2, 3, 3, 4])); // [1, 2, 3, 4]
// Flatten array
function flatten(arr) {
return arr.reduce((acc, item) =>
Array.isArray(item)
? [...acc, ...flatten(item)]
: [...acc, item],
[]
);
}
console.log(flatten([1, [2, [3, [4]]]])); // [1, 2, 3, 4]
🚀 Esempio Completo: Shopping Cart
// === DATI ===
const prodotti = [
{ id: 1, nome: "Laptop", prezzo: 999, categoria: "elettronica" },
{ id: 2, nome: "Mouse", prezzo: 25, categoria: "elettronica" },
{ id: 3, nome: "Tastiera", prezzo: 75, categoria: "elettronica" },
{ id: 4, nome: "Monitor", prezzo: 300, categoria: "elettronica" }
];
// === CART MANAGER ===
class CartManager {
constructor(initialState = { items: [], totale: 0 }) {
this.state = initialState;
}
// Aggiungi prodotto (immutabile)
addItem({ id, nome, prezzo }, quantità = 1) {
const existingItem = this.state.items.find(item => item.id === id);
if (existingItem) {
// Update quantità esistente
this.state = {
...this.state,
items: this.state.items.map(item =>
item.id === id
? { ...item, quantità: item.quantità + quantità }
: item
)
};
} else {
// Aggiungi nuovo item
const newItem = { id, nome, prezzo, quantità };
this.state = {
...this.state,
items: [...this.state.items, newItem]
};
}
this.updateTotale();
return this.state;
}
// Rimuovi prodotto
removeItem(id) {
this.state = {
...this.state,
items: this.state.items.filter(item => item.id !== id)
};
this.updateTotale();
return this.state;
}
// Update quantità
updateQuantity(id, quantità) {
if (quantità <= 0) {
return this.removeItem(id);
}
this.state = {
...this.state,
items: this.state.items.map(item =>
item.id === id
? { ...item, quantità }
: item
)
};
this.updateTotale();
return this.state;
}
// Calcola totale
updateTotale() {
this.state = {
...this.state,
totale: this.state.items.reduce(
(acc, { prezzo, quantità }) => acc + (prezzo * quantità),
0
)
};
}
// Applica sconto
applyDiscount(percentage) {
const sconto = this.state.totale * (percentage / 100);
return {
...this.state,
totale: this.state.totale - sconto,
sconto: {
percentage,
amount: sconto
}
};
}
// Clear cart
clear() {
this.state = { items: [], totale: 0 };
return this.state;
}
// Get state
getState() {
return { ...this.state }; // Copia immutabile
}
// Get summary
getSummary() {
const { items, totale } = this.state;
return {
numeroArticoli: items.reduce((acc, { quantità }) => acc + quantità, 0),
numeroProdotti: items.length,
totale,
items: items.map(({ nome, quantità, prezzo }) => ({
nome,
quantità,
subtotale: prezzo * quantità
}))
};
}
}
// === USO ===
const cart = new CartManager();
// Destructuring prodotti
const [laptop, mouse, tastiera, monitor] = prodotti;
// Aggiungi con destructuring
console.log("=== AGGIUNGI PRODOTTI ===");
cart.addItem(laptop, 1);
cart.addItem(mouse, 2);
cart.addItem(tastiera, 1);
// State corrente con destructuring
const { items, totale } = cart.getState();
console.log("Totale:", totale);
console.log("Items:", items.length);
// Update quantità
console.log("\n=== UPDATE QUANTITÀ ===");
cart.updateQuantity(2, 3); // Mouse: 2 → 3
// Summary con destructuring
const { numeroArticoli, numeroProdotti, items: summary } = cart.getSummary();
console.log(`Articoli: ${numeroArticoli}`);
console.log(`Prodotti diversi: ${numeroProdotti}`);
summary.forEach(({ nome, quantità, subtotale }) => {
console.log(`${nome} x${quantità} = €${subtotale}`);
});
// Applica sconto con destructuring
console.log("\n=== APPLICA SCONTO ===");
const stateWithDiscount = cart.applyDiscount(10);
const {
totale: totaleFinale,
sconto: { percentage, amount }
} = stateWithDiscount;
console.log(`Sconto ${percentage}%: -€${amount.toFixed(2)}`);
console.log(`Totale finale: €${totaleFinale.toFixed(2)}`);
// Filtra per categoria con destructuring
console.log("\n=== SOLO ELETTRONICA ===");
const elettronica = prodotti.filter(({ categoria }) => categoria === "elettronica");
console.log(`Prodotti elettronica: ${elettronica.length}`);
// Create order con spread
console.log("\n=== CREA ORDINE ===");
const order = {
id: Date.now(),
data: new Date(),
...cart.getState(),
cliente: {
nome: "Mario Rossi",
email: "mario@example.com"
},
spedizione: {
indirizzo: "Via Roma 10",
città: "Milano"
}
};
// Destructuring ordine
const {
id: orderId,
data: dataOrdine,
totale: totaleOrdine,
cliente: { nome: nomeCliente },
spedizione: { città: cittàSpedizione }
} = order;
console.log(`Ordine #${orderId}`);
console.log(`Cliente: ${nomeCliente}`);
console.log(`Spedizione: ${cittàSpedizione}`);
console.log(`Totale: €${totaleOrdine.toFixed(2)}`);
// Output:
// === AGGIUNGI PRODOTTI ===
// Totale: 1124
// Items: 3
//
// === UPDATE QUANTITÀ ===
// Articoli: 5
// Prodotti diversi: 3
// Laptop x1 = €999
// Mouse x3 = €75
// Tastiera x1 = €75
//
// === APPLICA SCONTO ===
// Sconto 10%: -€112.40
// Totale finale: €1011.60
//
// === SOLO ELETTRONICA ===
// Prodotti elettronica: 4
//
// === CREA ORDINE ===
// Ordine #1735823456789
// Cliente: Mario Rossi
// Spedizione: Milano
// Totale: €1124.00
- Array Destructuring -
const [laptop, mouse] = prodotti - Object Destructuring -
const { items, totale } = state - Nested Destructuring -
cliente: { nome } - Spread Objects -
{ ...state, totale } - Spread Arrays -
[...items, newItem] - Shorthand Properties -
{ nome, prezzo } - Arrow Functions -
item => item.id === id - Destructuring Parameters -
({ prezzo, quantità })
✅ Best Practices
// ✅ CHIARO
function creaUtente({ nome, email, età = 18 }) {
return { nome, email, età };
}
// ❌ CONFUSO
function creaUtente(nome, email, età) {
// Quale parametro è quale?
}
// ✅ IMMUTABILE
const newState = { ...state, count: state.count + 1 };
// ❌ MUTABILE (evita in React/Redux!)
state.count++;
// ✅ Array immutabile
const newArr = [...arr, newItem];
// ❌ Array mutabile
arr.push(newItem);
// ✅ Con default values
function config({ timeout = 5000, retries = 3 } = {}) {
// Funziona anche senza argomenti
}
config(); // Usa tutti i default
config({ timeout: 10000 }); // Sovrascrive timeout
const obj = {
name: "Mario",
address: { city: "Roma" }
};
// ⚠️ Shallow copy - nested objects condivisi!
const copy = { ...obj };
copy.address.city = "Milano";
console.log(obj.address.city); // "Milano" (modificato!)
// ✅ Deep copy per nested
const deepCopy = {
...obj,
address: { ...obj.address }
};
// O usa JSON (limitato)
const deepCopy2 = JSON.parse(JSON.stringify(obj));
- Estrai per posizione
- Swap variables
- Rest pattern
- Estrai per nome
- Rinomina variabili
- Nested objects
- Espandi elementi
- Copia/merge
- Immutabilità
- Parametri funzione
- Immutable updates
- Default values