Le due strutture dati pi utilizzate in JavaScript sono Object e Array.
- Gli oggetti ci consentono di creare ununica entit che memorizza elementi nel formato chiave-valore
- Gli array ci consentono di raccogliere elementi in elenchi ordinati.
A volte, quando li passiamo ad una funzione, potrebbe non essere necessario tutto loggetto/array, ma solo una parte di esso.
Lassegnamento di destrutturazione (Destructuring assignment) una speciale sintassi che ci consente di spacchettare oggetti o array in gruppi di variabili; questo a volte risulta molto conveniente.
La destrutturazione funziona alla grande anche con funzioni complesse che hanno molti parametri, valori predefiniti e cos via. Presto lo vedremo.
Destrutturazione di un array
Ecco un esempio di come un array viene destrutturato in variabili:
// abbiamo un array con nome e cognome
let arr = ["John", "Smith"]
// assegnamento di destrutturazione
// imposta firstName = arr[0]
// e surname = arr[1]
let [firstName, surname] = arr;
alert(firstName); // John
alert(surname); // Smith
Ora possiamo lavorare con le variabili invece che con gli elementi dellarray.
Risulta utilissima se combinata con split o altri metodi che ritornano un array:
let [firstName, surname] = "John Smith".split(' ');
alert(firstName); // John
alert(surname); // Smith
Come puoi vedere, la sintassi semplice. Ci sono per molti dettagli peculiari. Vediamo altri esempi, per capirlo meglio.
Viene chiamato assegnamento di destrutturazione perch destruttura copiando gli elementi allinterno di variabili. Ma larray in s non viene modificato.
E solo un modo breve per scrivere:
// let [firstName, surname] = arr;
let firstName = arr[0];
let surname = arr[1];
Gli elementi indesiderati dellarray possono essere ignorati tramite una virgola aggiuntiva:
// il secondo elemento non necessario
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert( title ); // Consul
Nel codice sopra, il secondo elemento viene saltato, il terzo viene assegnato a title, il resto degli elementi vengono ignorati (visto che per loro non ci sono variabili).
In realt, possiamo utilizzarlo con qualsiasi iterabile, non solamente con un array:
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
Funziona, perch internamente unassegnazione di destrutturazione lavora iterando sul valore a destra. E una specie di zucchero sintattico per chiamare for..of sul valore a destra di =, assegnandone i valori.
Possiamo inserire qualsiasi cosa assegnabile a sinistra.
Ad esempio, la propriet di un oggetto:
let user = {};
[user.name, user.surname] = "John Smith".split(' ');
alert(user.name); // John
alert(user.surname); // Smith
Nel capitolo precedente abbiamo visto il metodo Object.entries(obj).
Possiamo utilizzarlo con la destrutturazione per eseguire cicli sulle coppie chiave/valore di un oggetto:
let user = {
name: "John",
age: 30
};
// ciclo su chiavi/valori
for (let [key, value] of Object.entries(user)) {
alert(`${key}:${value}`); // name:John, poi age:30
}
Un codice simile usando Map pi semplice, visto che iterabile:
let user = new Map();
user.set("name", "John");
user.set("age", "30");
// Map itera le coppie [key, value], molto comodo per la destrutturazione
for (let [key, value] of user) {
alert(`${key}:${value}`); // name:John, then age:30
}
c un trucco molto conosciuto per scambiare i valori di due variabili usando lassegnamento di destrutturazione:
let guest = "Jane";
let admin = "Pete";
// Scambio dei valori: rende guest=Pete, admin=Jane
[guest, admin] = [admin, guest];
alert(`${guest} ${admin}`); // Pete Jane (scambiati con successo!)
Nellesempio creiamo un array temporaneo con due varibili e lo destrutturiamo immediatamente invertendo lordine delle variabili. Nello stesso modo potremmo scambiare i valori di pi variabili.
Loperatore rest
Di solito, se larray pi lungo della lista a sinistra, gli elementi in eccesso vengono ignorati.
Ad esempio, qui vengono presi solo i primi 2 elementi, i restanti vengono semplicemente ignorati:
let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert(name1); // Julius
alert(name2); // Caesar
// Gli elementi successivi non vengono assegnati
Se vogliamo ottenere anche tutto ci che segue, possiamo aggiungere un altro parametro che raccoglie il resto utilizzando tre punti " ... ":
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// rest un array con gli elementi a destra, partendo dal terzo
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2
La variabile rest un array con i valori rimanenti dellarray a destra.
Possiamo utilizzare qualsiasi altro nome di variabile al posto di rest; sufficiente accertarsi di inserire i tre punti prima del nome e di posizionarlo alla fine nellassegnamento di destrutturazione.
let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// now titles = ["Consul", "of the Roman Republic"]
Valori di default
Se ci sono meno elementi nellarray delle variabili da assegnare, non ci sar alcun errore. I valori assenti vengono considerati undefined:
let [firstName, surname] = [];
alert(firstName); // undefined
alert(surname); // undefined
Se volessimo assegnare un nostro valore di default, potremmo indicarlo con la sintassi =:
// valori di default
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
alert(name); // Julius (dall'array)
alert(surname); // Anonymous (valore di default)
I valori di default possono essere anche espressioni complesse o chiamate a funzione. Verranno presi in considerazione solamente se non verr fornito alcun valore.
Ad esempio, qui usiamo la funzione prompt per due defaults:
// viene eseguito solo il prompt per il cognome
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
alert(name); // Julius (dall'array)
alert(surname); // qualsiasi cosa provenga dal prompt
Attenzione: la funzione prompt verr eseguita solo per il valore mancante (surname).
Destrutturazione di oggetti
Lassegnamento di destrutturazione funziona anche con gli oggetti.
La sintassi :
let {var1, var2} = {var1:, var2:}
Abbiamo un oggetto alla destra dellassegnazione, che vogliamo dividere in variabili. Nel lato sinistro abbiamo un pattern di propriet corrispondenti. In questo semplice caso, abbiamo una lista di variabili raggruppate tra parentesi {...}.
Ad esempio:
let options = {
title: "Menu",
width: 100,
height: 200
};
let {title, width, height} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
Le propriet options.title, options.width e options.height vengono assegnate alle variabili corrispondenti.
Lordine non ha importanza. Questo codice funzionerebbe comunque:
// cambiato l'ordine delle propriet in let {...}
let {height, width, title} = { title: "Menu", height: 200, width: 100 }
Il pattern a sinistra potrebbe essere anche pi complesso e specificare una mappatura tra propriet e variabili.
Se volessimo assegnare una propriet ad una variabile con un altro nome, ad esempio la propriet options.width ad una variabile chiamata w, allora possiamo specificarlo con i due punti:
let options = {
title: "Menu",
width: 100,
height: 200
};
// { sourceProperty: targetVariable }
let {width: w, height: h, title} = options;
// width -> w
// height -> h
// title -> title
alert(title); // Menu
alert(w); // 100
alert(h); // 200
I due punti specificano cosa : va dove. Nellesempio sopra la propriet width va in w, la propriet height va in h, e title viene assegnata ad una variabile con lo stesso nome.
Per delle potenziali propriet mancanti possiamo impostare dei valori di default utilizzando "=", come nellesempio:
let options = {
title: "Menu"
};
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
Proprio come nel caso degli array o dei parametri di funzione, i valori di default possono essere espressioni pi complesse o chiamate a funzioni. Questi verranno valutati solo nel caso in cui il valore non verr fornito.
Il codice richieder tramite prompt la width (larghezza), ma non il title (titolo):
let options = {
title: "Menu"
};
let {width = prompt("width?"), title = prompt("title?")} = options;
alert(title); // Menu
alert(width); // (qualsiasi cosa arrivi dal prompt)
Possiamo anche combinare entrambi, i due punti e luguaglianza:
let options = {
title: "Menu"
};
let {width: w = 100, height: h = 200, title} = options;
alert(title); // Menu
alert(w); // 100
alert(h); // 200
Se abbiamo un oggetto complesso con molte propriet, possiamo estrarre solamente ci che ci serve:
let options = {
title: "Menu",
width: 100,
height: 200
};
// estraiamo solamente title come propriet
let { title } = options;
alert(title); // Menu
Il modello rest
Cosa succede se loggetto possiede pi propriet delle variabili da noi fornite? Possiamo prenderne solamente alcune ed assegnare tutto ci che avanza da unaltra parte?
La specifica per lutilizzo delloperatore rest ... fa quasi parte dello standard, ma molti browser non lo supportano ancora.
Appare cosi:
let options = {
title: "Menu",
height: 200,
width: 100
};
// title = propriet con il titolo
// rest = oggetto con il resto dei parametri
let {title, ...rest} = options;
// ora title="Menu", rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100
letNegli esempi sopra le variabili vengono dichiarate appena prima di essere assegnate: let {} = {}. Ovviamente, potremmo anche utilizzare delle variabili gi esistenti. Ma c un tranello.
Questo non funzionerebbe:
let title, width, height;
// errore in questa riga
{title, width, height} = {title: "Menu", width: 200, height: 100};
Il problema che JavaScript tratta {...} come un blocco di codice. Questo blocco di codice pu essere utilizzato per raggruppare istruzioni, come nellesempio:
{
// un blocco di codice
let message = "Hello";
// ...
alert( message );
}
Per informare JavaScript che non ci troviamo in un blocco di codice, possiamo raggruppare lintera assegnazione tra parentesi (...):
let title, width, height;
// ora funziona
({title, width, height} = {title: "Menu", width: 200, height: 100});
alert( title ); // Menu
Destrutturazione annidata
Se un oggetto o un array contiene altri oggetti o array, possiamo utilizzare sequenze (pattern) di estrazione pi complesse per andare pi in profondit con lestrazione.
Nel codice sotto options possiede un ulteriore oggetto nella propriet size ed un array nella propriet items. Il pattern alla sinistra dellassegnazione ha la stessa struttura:
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true // qualche extra che non destruttureremo
};
// destructuring assignment split in multiple lines for clarity
let {
size: { // mettiamo size qui
width,
height
},
items: [item1, item2], // assegniamo gli items qui
title = "Menu" // non presente nell'oggetto (viene utilizzato il valore di default)
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
Lintero oggetto options ad eccezione di extra, il quale non viene menzionato, viene assegnato alle corrispondenti variabili.
Infine, abbiamo width, height, item1, item2 e title che assumono il valore di default.
Nota che non ci sono variabili per size e items: prendiamo invece il loro contenuto. Questo accade spesso con lassegnamento di destrutturazione. Abbiamo un oggetto complesso e vogliamo estrarre solamente ci di cui abbiamo bisogno.
Parametri di funzione intelligenti
Ci sono casi in cui una funzione pu accettare pi parametri, molti dei quali opzionali. Questo vero specialmente per le interfacce utente. Immaginate una funzione che crea un menu. Pu avere una larghezza, unaltezza, un titolo, una lista di elementi e molto altro.
Vediamo un pessimo modo per scrivere questo tipo di funzioni:
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
Nella vita reale, il problema ricordarsi lordine degli argomenti. Solitamente gli IDE ci aiutano in questo, specialmente se il codice ben documentato, eppure Un ulteriore problema quello di chiamare una funzione nel caso in cui molti parametri ci vadano bene di default.
Come qui?
// undefined where default values are fine
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
E brutto a vedersi. E diventa illeggibile quando il numero di parametri aumenta.
La destrutturazione ci viene in soccorso!
Possiamo passare i parametri come un oggetto, e la funzione immediatamente lo destrutturizzer in variabili:
// passiamo l'oggetto alla funzione
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
// ...e immediatamente lo distribuisce alle variabili
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
// title, items presi da options,
// width, height valori di default
alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}
showMenu(options);
Possiamo anche utilizzare una destrutturazione pi complessa con oggetti annidati e diverse mappature:
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
function showMenu({
title = "Untitled",
width: w = 100, // width va su w
height: h = 200, // height va su h
items: [item1, item2] // il primo elemento va su item1, il secondo su item2
}) {
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
showMenu(options);
La sintassi la stessa dellassegnamento di destrutturazione:
function({
incomingProperty: varName = defaultValue
...
})
Da notare che la destrutturazione presuppone che showMenu() abbia un argomento. Se vogliamo tutti i valori di default, allora dovremmo specificare un oggetto vuoto:
showMenu({}); // ok, tutti i valori sono di default
showMenu(); // questo dar errore
Possiamo farlo ponendo {} come valore di default per lintera destrutturazione:
// parametri semplificati per chiarezza
function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
alert( `${title} ${width} ${height}` );
}
showMenu(); // Menu 100 200
Nel codice sopra, loggetto degli argomenti {} di default, quindi ci sar sempre qualcosa da destrutturare.
Riepilogo
-
Lassegnamento di destrutturazione ci consente di mappare un oggetto o un array passandolo a variabili.
-
La sintassi per gli oggetti:
let {prop : varName = default, ...rest} = objectQuesto significa che la propriet
propdovrebbe andare nella variabilevarNamee, se non esiste alcuna propriet, allora verr utilizzato il valore didefault. -
La sintassi per gli array:
let [item1 = default, item2, ...rest] = arrayIl primo elemento va in
item1; il secondo va initem2, tutti gli altri finiscono nellarrayrest. -
Per casi pi complessi, la parte sinistra deve possedere la stessa struttura di quella destra.
Commenti
<code>, per molte righe includile nel tag<pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)