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…
🎯 In Questa Lezione

  • 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.

1
Sintassi Base

Destructuring Base
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)

2
Rest Pattern (…rest)

Raccogli i rimanenti elementi in un array.

Rest Pattern
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}`);

3
Swap Variables (Scambia Valori)

Scambia valori senza variabile temporanea!

Swap con Destructuring
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)

4
Funzioni che Restituiscono Array

Return Array
// 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!

1
Sintassi Base

Object Destructuring Base
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

2
Destructuring Annidato

Estrai da oggetti dentro oggetti.

Nested Destructuring
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"

3
Parametri Funzione con Destructuring

Pattern molto comune e utile! ⭐

Function Parameters
// ❌ 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

4
Rest Pattern con Oggetti

Object Rest
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 ...

1
Spread con Array

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

2
Spread con Oggetti

Object Spread
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

3
Spread in Chiamate Funzione

Function Call Spread
// 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!

📊 Differenze Rest vs Spread

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 vs Spread - Esempi
// 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

1
API / Fetch Responses

API Response Handling
// 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');

2
State Management Immutabile

Pattern fondamentale in React, Redux, ecc.

Immutable Updates
// 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! ✅

3
Utility Functions

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

Shopping Cart con ES6+ Features
// === 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

💡 ES6+ Features Usate
  • 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

🌟 Regole d'Oro

1
Usa Destructuring per Parametri Funzione

// ✅ CHIARO
function creaUtente({ nome, email, età = 18 }) {
    return { nome, email, età };
}

// ❌ CONFUSO
function creaUtente(nome, email, età) {
    // Quale parametro è quale?
}

2
Spread per Immutabilità

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

3
Default Values con Destructuring

// ✅ Con default values
function config({ timeout = 5000, retries = 3 } = {}) {
    // Funziona anche senza argomenti
}

config();  // Usa tutti i default
config({ timeout: 10000 });  // Sovrascrive timeout

4
Attenzione allo Shallow Copy

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

🎯 Riepilogo Lezione

Array Destructuring

  • Estrai per posizione
  • Swap variables
  • Rest pattern
Object Destructuring

  • Estrai per nome
  • Rinomina variabili
  • Nested objects
Spread Operator

  • Espandi elementi
  • Copia/merge
  • Immutabilità
Best Practices

  • Parametri funzione
  • Immutable updates
  • Default values

Lezione 15 del Corso JavaScript Completo

Torna in alto