Stessa sintassi, due compiti
I tre punti ... compaiono spesso nel codice JavaScript moderno, e fanno due cose opposte a seconda di dove si trovano. Una volta capito lo schema, ogni uso di ... rientra in una di due categorie:
- Rest:
...namedal lato di chi riceve raccoglie più valori in un unico array o oggetto. - Spread:
...valuedal lato di chi dà espande un array o un oggetto nei suoi singoli pezzi.
Questo è l'intero modello mentale. Il resto della pagina sono solo esempi di ciascuno, e gli schemi che userai di continuo.
Parametri rest: raccogliere argomenti
Un parametro rest nella definizione di una funzione raccoglie un numero qualsiasi di argomenti in un vero array:
nums è un array normale. Puoi usare .map, .filter, controllarne la .length, passarlo a un'altra funzione: tutto ciò che fanno gli array.
I parametri rest si possono mescolare con i parametri normali, ma il parametro rest deve venire per ultimo:
label prende il primo argomento; tutto il resto finisce in items. Scrivere il parametro rest in una posizione diversa dall'ultima è un errore di sintassi.
Rest e il vecchio oggetto arguments
Il codice JavaScript più vecchio usa una variabile magica chiamata arguments dentro le funzioni normali. Sembra un array ma non lo è, il che significa che i metodi degli array non funzionano direttamente su di essa. I parametri rest la sostituiscono in modo pulito:
Le arrow function non hanno nemmeno un oggetto arguments, quindi i parametri rest sono l'unico modo per accettare un numero variabile di argomenti. Nel codice nuovo preferisci sempre ...args.
Spread nelle chiamate di funzione
Lo spread fa il contrario: prende un array e lo scompone in singoli argomenti nel punto della chiamata.
Math.max accetta singoli numeri, non un array. Prima dello spread avresti scritto Math.max.apply(null, nums). Ora basta un ... e hai finito.
Nota come lo stesso identico ... sia rest nella definizione della funzione e spread nella chiamata: è la posizione a dirti quale dei due è.
Spread nei letterali di array
Fare lo spread dentro un letterale di array copia o combina array:
[...a] ti dà un array nuovo con gli stessi elementi, utile quando vuoi ordinare o modificare senza toccare l'originale:
scores resta intatto perché .sort è stato eseguito sulla copia. Piccola abitudine, grande vantaggio quando scrivi codice che non deve avere effetti collaterali a sorpresa.
Spread nei letterali di oggetto
Lo spread funziona anche sugli oggetti semplici, unendo le loro proprietà in un oggetto nuovo:
Vincono le chiavi che vengono dopo. updates.age sovrascrive user.age, e city si aggiunge al resto. L'ordine degli spread determina il risultato: tienilo a mente quando sovrapponi valori predefiniti e personalizzazioni:
Prima i valori predefiniti, poi le scelte dell'utente. L'utente vince su fontSize ed eredita theme.
La trappola della copia superficiale
Lo spread copia un solo livello. Gli oggetti e gli array annidati restano condivisi tra l'originale e la copia:
Entrambi gli array mostrano il nuovo tag perché copy.tags e original.tags sono lo stesso array. Lo spread non ha clonato la lista annidata: ha solo copiato il riferimento.
Per una vera copia profonda di dati semplici, usa structuredClone:
Ora i due array sono indipendenti. structuredClone è integrato nei browser moderni e in Node, gestisce le strutture annidate ed è la scelta giusta ogni volta che una copia "superficiale" non basta.
Rest nella destrutturazione
Il rest funziona anche nella destrutturazione, dove raccoglie gli elementi o le proprietà rimanenti:
Estrarre qualche campo e tenere il resto in un unico oggetto è uno schema comune quando inoltri props, togli campi sensibili o costruisci versioni modificate dei dati:
password viene estratta (e ignorata); safe contiene tutto il resto. Nessuna modifica, nessuna copia manuale.
Riepilogo veloce
...namein un elenco di parametri o in un pattern di destrutturazione è rest: raccoglie....valuein una chiamata di funzione, in un letterale di array o di oggetto è spread: espande.- Le copie con lo spread sono superficiali. Le strutture annidate restano condivise. Usa
structuredCloneper le copie profonde. - I parametri rest sono veri array: usali al posto di
arguments. - Negli oggetti gli spread successivi sovrascrivono quelli precedenti, ed è così che costruisci valori predefiniti più personalizzazioni.
Prossimo passo: le closure
In JavaScript le funzioni non si limitano a ricevere input e restituire output: ricordano anche lo scope in cui sono state definite. Questa memoria si chiama closure, ed è il meccanismo dietro le callback, le factory e la maggior parte degli schemi che incontrerai nella prossima pagina.
Domande frequenti
Qual è la differenza tra rest e spread in JavaScript?
Usano la stessa sintassi ... ma fanno lavori opposti. Il rest raccoglie più valori in un unico array: compare negli elenchi di parametri delle funzioni e nella destrutturazione. Lo spread espande un iterabile o un oggetto nei suoi pezzi: compare nelle chiamate di funzione, nei letterali di array e nei letterali di oggetto. Se ... sta dal lato di chi riceve, è rest; se sta dal lato di chi dà, è spread.
Come funzionano i parametri rest in una funzione?
Un parametro rest come function sum(...nums) raccoglie ogni argomento passato alla funzione in un vero array chiamato nums. Deve essere l'ultimo parametro dell'elenco. A differenza del vecchio oggetto arguments, un parametro rest è un vero array, quindi .map, .filter e .reduce funzionano direttamente.
Lo spread operator fa una copia profonda?
No. Lo spread copia un solo livello: è una copia superficiale. { ...user } ti dà un nuovo oggetto con le stesse chiavi di primo livello, ma gli oggetti e gli array annidati restano riferimenti condivisi. Per una copia profonda usa structuredClone(value), oppure passa per JSON se si tratta di dati semplici.