Assegnamento di Destrutturazione in Javascript

Concetti Chiave
  • L'assegnazione destrutturata in JavaScript consente di estrarre valori da array e oggetti in modo conciso e leggibile.
  • Si può usare l'assegnazione destrutturata per inizializzare variabili, semplificando il codice.
  • L'assegnazione destrutturata può essere utilizzata con array annidati e oggetti iterabili.
  • È possibile raccogliere valori rimanenti in una variabile usando i tre punti (...) nella sintassi di assegnazione.
  • L'assegnazione destrutturata può essere utilizzata anche nei cicli for per iterare su coppie nome/valore di oggetti.
  • La destrutturazione può essere applicata a qualsiasi oggetto iterabile, non solo a array, rendendola una caratteristica versatile del linguaggio.

Assegnazione destrutturata

ES6 implementa un tipo di sintassi composta per dichiarazione e assegnazione nota come assegnazione destrutturata. In un'assegnazione destrutturata, il valore sul lato destro del segno di uguale è un array o oggetto (un valore "strutturato"), e il lato sinistro specifica uno o più nomi di variabili usando una sintassi che imita la sintassi letterale di array e oggetti. Quando avviene un'assegnazione destrutturata, uno o più valori vengono estratti ("destrutturati") dal valore sulla destra e memorizzati nelle variabili nominate sulla sinistra. L'assegnazione destrutturata è forse più comunemente usata per inizializzare variabili come parte di una dichiarazione const, let, o var, ma può anche essere fatta in espressioni di assegnazione regolari (con variabili che sono già state dichiarate). E, come si vedrà nelle lezioni sulle funzioni, la destrutturazione può anche essere usata quando si definiscono i parametri di una funzione.

Ecco semplici assegnazioni destrutturate usando array di valori:

let [x,y] = [1,2];  // Stesso di let x=1, y=2
[x,y] = [x+1,y+1];  // Stesso di x = x + 1, y = y + 1
[x,y] = [y,x];      // Scambia il valore delle due variabili
[x,y]               // => [3,2]: i valori incrementati e scambiati

Si noti come l'assegnazione destrutturata rende facile lavorare con funzioni che restituiscono array di valori:

// Converte coordinate [x,y] in coordinate polari [r,theta]
function aPolare(x, y) {
    return [Math.sqrt(x*x+y*y), Math.atan2(y,x)];
}

// Converte coordinate polari in coordinate cartesiane
function aCartesiane(r, theta) {
    return [r*Math.cos(theta), r*Math.sin(theta)];
}

let [r,theta] = aPolare(1.0, 1.0);  // r == Math.sqrt(2); theta == Math.PI/4
let [x,y] = aCartesiane(r,theta);   // [x, y] == [1.0, 1,0]

Si è visto che variabili e costanti possono essere dichiarate come parte dei vari cicli for di JavaScript. È possibile usare la destrutturazione di variabili anche in questo contesto. Ecco un codice che itera sulle coppie nome/valore di tutte le proprietà di un oggetto e usa l'assegnazione destrutturata per convertire quelle coppie da array a due elementi in variabili individuali:

let o = { x: 1, y: 2 }; // L'oggetto su cui itereremo
for(const [nome, valore] of Object.entries(o)) {
    console.log(nome, valore); // Stampa "x 1" e "y 2"
}

Il numero di variabili sulla sinistra di un'assegnazione destrutturata non deve corrispondere al numero di elementi dell'array sulla destra. Le variabili extra sulla sinistra sono impostate a undefined, e i valori extra sulla destra sono ignorati. La lista di variabili sulla sinistra può includere virgole extra per saltare certi valori sulla destra:

let [x,y] = [1];     // x == 1; y == undefined
[x,y] = [1,2,3];     // x == 1; y == 2
[,x,,y] = [1,2,3,4]; // x == 2; y == 4

Se si vogliono raccogliere tutti i valori inutilizzati o rimanenti in una singola variabile quando si destruttura un array, si usano tre punti (...) prima dell'ultimo nome di variabile sul lato sinistro:

let [x, ...y] = [1,2,3,4];  // y == [2,3,4]

L'assegnazione destrutturata può essere usata con array annidati. In questo caso, il lato sinistro dell'assegnazione dovrebbe assomigliare a un letterale di array annidato:

let [a, [b, c]] = [1, [2,2.5], 3]; // a == 1; b == 2; c == 2.5

Una caratteristica potente della destrutturazione di array è che non richiede effettivamente un array! Si può usare qualsiasi oggetto iterabile (un concetto che si approfondirà più avanti) sul lato destro dell'assegnazione; qualsiasi oggetto che può essere usato con un ciclo for/of può anche essere destrutturato:

let [primo, ...resto] = "Ciao"; // primo == "C"; resto == ["i","a","o"]

L'assegnazione destrutturata può anche essere eseguita quando il lato destro è un valore oggetto. In questo caso, il lato sinistro dell'assegnazione assomiglia a un letterale di oggetto: una lista separata da virgole di nomi di variabili dentro parentesi graffe:

let trasparente = {r: 0.0, g: 0.0, b: 0.0, a: 1.0}; // Un colore RGBA
let {r, g, b} = trasparente;  // r == 0.0; g == 0.0; b == 0.0

L'esempio seguente copia funzioni globali dell'oggetto Math in variabili, il che potrebbe semplificare codice che fa molta trigonometria:

// Stesso di const sin=Math.sin, cos=Math.cos, tan=Math.tan
const {sin, cos, tan} = Math;

Si noti nel codice qui che l'oggetto Math ha molte proprietà oltre alle tre che sono destrutturate in variabili individuali. Quelle che non sono nominate sono semplicemente ignorate. Se il lato sinistro di questa assegnazione avesse incluso una variabile il cui nome non era una proprietà di Math, quella variabile sarebbe semplicemente stata assegnata a undefined.

In ciascuno di questi esempi di destrutturazione di oggetti, si sono scelti nomi di variabili che corrispondono ai nomi delle proprietà dell'oggetto che si sta destrutturando. Questo mantiene la sintassi semplice e facile da capire, ma non è richiesto. Ciascuno degli identificatori sul lato sinistro di un'assegnazione destrutturata di oggetto può anche essere una coppia di identificatori separati da due punti, dove il primo è il nome della proprietà il cui valore deve essere assegnato e il secondo è il nome della variabile a cui assegnarlo:

// Stesso di const coseno = Math.cos, tangente = Math.tan;
const { cos: coseno, tan: tangente } = Math;

Spesso, la sintassi di destrutturazione di oggetti tende a diventare troppo complicata per essere utile quando i nomi delle variabili e i nomi delle proprietà non sono gli stessi, e si tende a evitarla in questo caso. Se si sceglie di usarla, si ricordi che i nomi delle proprietà sono sempre sulla sinistra dei due punti, sia nei letterali di oggetto che sulla sinistra di un'assegnazione destrutturata di oggetto.

L'assegnazione destrutturata diventa ancora più complicata quando è usata con oggetti annidati, o array di oggetti, o oggetti di array, ma è legale:

let punti = [{x: 1, y: 2}, {x: 3, y: 4}];     // Un array di due oggetti punto
let [{x: x1, y: y1}, {x: x2, y: y2}] = punti; // destrutturato in 4 variabili.
(x1 === 1 && y1 === 2 && x2 === 3 && y2 === 4) // => true

Oppure, invece di destrutturare un array di oggetti, potremmo destrutturare un oggetto di array:

let punti = { p1: [1,2], p2: [3,4] };         // Un oggetto con 2 proprietà array
let { p1: [x1, y1], p2: [x2, y2] } = punti;   // destrutturato in 4 variabili
(x1 === 1 && y1 === 2 && x2 === 3 && y2 === 4) // => true

Una sintassi di destrutturazione complessa come questa può essere difficile da scrivere e difficile da leggere, e si potrebbe essere meglio serviti scrivendo semplicemente le assegnazioni esplicitamente con codice tradizionale come let x1 = punti.p1[0];.

Comprendere Assegnazioni di Destrutturazione Complesse

Se ci si trova a lavorare con codice che utilizza assegnazioni di destrutturazione complesse, c'è una regolarità utile che può aiutare a dare senso ai casi complessi. Si pensi prima a un'assegnazione regolare (a valore singolo). Dopo che l'assegnazione è stata completata, si può prendere il nome della variabile dal lato sinistro dell'assegnazione e usarlo come espressione nel codice, dove verrà valutato a qualsiasi valore gli sia stato assegnato. La stessa cosa è vera per l'assegnazione di destrutturazione. Il lato sinistro di un'assegnazione di destrutturazione sembra un array literal o un object literal. Dopo che l'assegnazione è stata completata, il lato sinistro funzionerà effettivamente come un array literal o object literal valido altrove nel codice. Si può verificare di aver scritto correttamente un'assegnazione di destrutturazione provando a usare il lato sinistro sul lato destro di un'altra espressione di assegnazione:

// Inizia con una struttura dati e una destrutturazione complessa
let punti = [{x: 1, y: 2}, {x: 3, y: 4}];
let [{x: x1, y: y1}, {x: x2, y: y2}] = punti;

// Verifica la tua sintassi di destrutturazione invertendo l'assegnazione
let punti2 = [{x: x1, y: y1}, {x: x2, y: y2}]; // punti2 == punti