Le funzioni sono valori
In JavaScript, una funzione è un valore come qualsiasi altro. Puoi salvarla in una variabile, metterla in un array, passarla a un'altra funzione o restituirla da una funzione. Questo fatto da solo apre un intero stile di programmazione:
Quando ti abitui a trattare le funzioni come valori, le funzioni di ordine superiore smettono di sembrare misteriose. Una funzione di ordine superiore è semplicemente una funzione che riceve una funzione come argomento, restituisce una funzione, oppure entrambe le cose. Questa è tutta la definizione.
Ricevere una funzione come argomento
La forma più comune: una funzione che accetta una callback e la esegue per te. Le hai già usate senza pensarci.
forEach è di ordine superiore: prende la tua funzione e la chiama una volta per ogni elemento. setTimeout è di ordine superiore: prende la tua funzione e la chiama dopo un certo ritardo. Tu ti concentri su cosa fare; loro si occupano di quando e quante volte.
Scriverne una tua è la stessa cosa. Ecco una piccola funzione che esegue una callback solo se una condizione è vera:
action è un parametro che contiene una funzione. Chiamarlo con action() esegue qualunque cosa sia stata passata.
Le tre che userai davvero: map, filter, reduce
Gli array hanno metodi di ordine superiore che sostituiscono la maggior parte dei cicli for che altrimenti scriveresti. Impara questi tre e una grande quantità di codice quotidiano diventa più corta e più chiara.
map: trasformare ogni elemento
map chiama la tua funzione una volta per elemento e raccoglie i valori restituiti in un nuovo array. Stessa lunghezza, contenuto trasformato. L'array originale non viene modificato.
filter: tenere quelli che corrispondono
filter tiene gli elementi per cui la callback restituisce un valore truthy e scarta gli altri. Il risultato è un nuovo array, eventualmente più corto.
reduce: ridurre l'array a un solo valore
reduce è quella più generale. La callback riceve l'accumulatore corrente e l'elemento attuale; qualunque cosa tu restituisca diventa l'accumulatore successivo. Il secondo argomento (qui 0) è il valore iniziale.
Puoi concatenarle. È qui che questo stile ripaga:
Leggilo dall'alto in basso: tieni gli ordini pagati, estrai il prezzo, somma tutto. Nessun ciclo, nessun contatore mutabile, nessun errore di uno di cui preoccuparsi.
Restituire una funzione da una funzione
L'altra metà dell'ordine superiore. Una funzione che costruisce e restituisce un'altra funzione:
multiplyBy(2) viene eseguita una volta e restituisce una funzione nuova di zecca. Quella nuova funzione si ricorda ancora di factor: è una closure, e avrà una pagina tutta sua più avanti. Per ora il concetto chiave è questo: chiamare multiplyBy con argomenti diversi ti dà funzioni specializzate diverse costruite dallo stesso modello.
Questo schema compare ovunque:
Una definizione, due funzioni riutilizzabili. Meglio che scrivere warn e info a mano e doverle tenere allineate.
Funzioni con nome e callback inline
Puoi passare una arrow function inline oppure passare una funzione per nome. Funzionano entrambe: scegli quella che si legge meglio.
Quando passi isEven (senza parentesi), stai consegnando la funzione stessa. Aggiungere () la chiamerebbe subito e passerebbe il risultato, un errore comune tra i principianti:
nums.filter(isEven); // correct: passes the function
nums.filter(isEven()); // wrong: calls isEven with no args, passes the result
Se la callback comincia a superare un paio di righe, estraila e dagli un nome. Il codice intorno di solito ne guadagna in leggibilità.
Un esempio completo
Le funzioni di ordine superiore danno il meglio quando componi piccoli pezzi. Supponi di avere una lista di prodotti e di volere i nomi degli articoli economici e disponibili, in maiuscolo:
Ogni helper ha un solo compito. Ogni metodo degli array fa una sola trasformazione. La pipeline si legge come una specifica di ciò che vuoi, non come la descrizione di come scorrere i dati.
Quando non usarle
I metodi di ordine superiore sono ottimi, ma non sostituiscono un ciclo in ogni situazione:
- Se devi fermarti a metà, un
foro unfor...ofconbreakè più chiaro che cercare di uscire da unforEach. - Se dentro la callback fai qualcosa di asincrono,
mapeforEachnon lo aspettano. Usafor...ofconawait, oppurePromise.allconmap. - Se la callback modifica uno stato condiviso, ti stai allontanando dai punti di forza di questo stile. Usa un ciclo normale o riorganizza il codice per restituire nuovi valori.
Usati dove hanno senso, map, filter e reduce eliminano la maggior parte del codice ripetitivo dei cicli. Usati ovunque, iniziano a peggiorare la leggibilità. Scegli lo strumento che rende più chiara l'intenzione.
Prossimo passo: gli oggetti
Le funzioni non sono gli unici valori su cui vale la pena costruire. Gli oggetti sono lo strumento principale di JavaScript per raggruppare dati e comportamenti correlati, e sono ciò di cui erano pieni la maggior parte degli array che hai appena filtrato e trasformato. Sono l'argomento della prossima pagina.
Domande frequenti
Cos'è una funzione di ordine superiore in JavaScript?
Una funzione di ordine superiore (higher-order function) è una funzione che fa almeno una di queste due cose: riceve un'altra funzione come argomento, oppure restituisce una funzione come risultato. Array.prototype.map, setTimeout e addEventListener sono tutte di ordine superiore: accettano una callback e la eseguono al posto tuo.
Qual è la differenza tra map, filter e reduce?
map trasforma ogni elemento e restituisce un nuovo array della stessa lunghezza. filter tiene gli elementi per cui la callback restituisce un valore truthy e restituisce un array (eventualmente più corto). reduce riduce l'array a un solo valore combinando gli elementi uno alla volta. Sono tutte e tre funzioni di ordine superiore che ricevono una callback.
Perché restituire una funzione da un'altra funzione?
Per costruire piccoli helper configurabili senza ripetere la logica. Una funzione come multiplyBy(n) restituisce una nuova funzione che moltiplica per n, quindi multiplyBy(2) e multiplyBy(10) ti danno due funzioni specializzate a partire da una sola definizione. Questo schema sfrutta le closure ed è comune negli event handler, nei middleware e nelle librerie di utilità.