Node.js: sfruttare a pieno le specifiche di ECMAScript

La somiglianza tra Node.js e JavaScript presenta diversi vantaggi. Il primo vantaggio e ovviamente quello di permettere a chi conosce JavaScript di realizzare pagine server-side per applicazioni enterprise. Il secondo vantaggio e quello di evitare i fastidiosi problemi di sviluppo cross-browser tipici di JavaScript. Per capire la questione conviene fare un passo indietro. Ai suoi albori JavaScript era soltanto uno dei tanti linguaggi di scripting per browser.

La somiglianza tra Node.js e JavaScript presenta diversi vantaggi. Il primo vantaggio è ovviamente quello di permettere a chi conosce JavaScript di realizzare pagine server-side per applicazioni enterprise. Il secondo vantaggio è quello di evitare i fastidiosi problemi di sviluppo cross-browser tipici di JavaScript. Per capire la questione conviene fare un passo indietro. Ai suoi albori JavaScript era soltanto uno dei tanti linguaggi di scripting per browser. Col passare del tempo, verso la fine degli anni ’90, l’associazione ECMA ha prodotto delle specifiche per standardizzare le diverse versioni di JavaScript. Tali specifiche sono note come ECMA-nnn, dove “nnn” indica il numero della specifica. Ad esempio, l’ultima specifica riconosciuta è la ECMA-262 edizione 5.1.
Nell’ambito della programmazione JavaScript queste specifiche hanno un ruolo simile a quello che il consorzio W3C ha nell’ambito del HTML. Le specifiche ECMA dovrebbero (e qui in corsivo è d’obbligo) essere usate dai produttori di browser per implementare il motore JavaScript del browser. Purtroppo le specifiche non vengono mai rispettate in toto, ed è proprio per questo motivo che ogni browser gestisce JavaScript a modo suo.

Il vantaggio di lavorare con Node.js è quello di evitare completamente problemi di questo tipo. Il codice Node.js è compilato dall’interprete V8 invece che essere interpretato da un browser. Quando installiamo Node.js siamo sicuri di usare sempre la stessa implementazione del motore, un po’ come se usassimo tutti lo stesso browser. Possono esistere differenze tra versione e versione di Node. Js, ma queste sono di gran lunga minori rispetto alle differenze che riscontriamo tra un browser e l’altro.

Intelligentemente, i creatori di Node.js hanno deciso di rispettare il più possibile le specifiche di ECMAScript, che può essere pensato come il papà di tutti i dialetti JavaScript. Nella pratica ciò significa che istruzioni JavaScript sconsigliate quando scriviamo codice per un browser sono invece supportate quando lavoriamo con Node.js. La metodologia di sviluppo JavaScript può essere quindi allargata (cioè ammorbidita) quando sviluppiamo con Node.js, perché possiamo usare molte funzioni ECMAScript che solitamente dobbiamo tralasciare. Un elenco delle funzionalità di questo tipo è disponibile qui. Abbiamo pensato di collaudare e documentare, per mezzo di esempi pratici, quelle che sembrano essere le funzioni più interessanti di questo tipo.

Dal punto di vista pratico le funzioni che molto probabilmente ci interessano riguardano soprattutto la gestione degli array. Nelle prossime pagine introduciamo e discutiamo alcune delle funzioni di ECMAScript che solitamente non conviene usare lavorando con JavaScript. A conferma di quanto appena detto invitiamo a controllare le API degli array JavaScript riconosciute dal W3C, disponibile qui: tutte le funzioni che discuteremo nelle prossime pagine non appartengono a tale elenco proprio perché il loro utilizzo è sconsigliato nel browser.

forEach() e filter()

forEach() e filter()

Vediamo quali sono le funzioni più interessanti che possiamo usare lavorando con Node.js, nonostante esse siano sconsigliate nell’ambito JavaScript. Ribadiamo il concetto: siccome Node.js gira su un motoreV8 di Google, l’utilizzo di queste funzioni è assolutamente sicuro, garantito, portabile e stabile. Usando queste funzioni non stiamo compiendo approssimazioni né scommettendo sulla tecnologia. Il supporto da parte di Node. Js delle specifiche di ECMAScript è ufficiale e possiamo farci affidamento.

Iniziamo considerando la funzione forEach, del tutto equivalente a quella presente in molti altri linguaggi di programmazione. La funzione permette di ciclare su tutti gli elementi dell’array, scorrendoli uno ad uno. La funzione accetta due argomenti: il primo verrà valorizzato con l’elemento corrente dell’array, il secondo con l’indice dell’elemento in questione. Data l’utilità di questa funzione usiamola per realizzare una funzione che ci tornerà comoda nei prossimi esempi:

per testarla eseguiamo la funzione su un paio di array, ad esempio

dove abbiamo definito i due array usando due sintassi diverse per ricordarci che sono entrambe valide. Eseguendo la funzione print_r sui due array otterremo come risultato la stampa di tutti gli elementi degli array all’interno della shell di esecuzione. Quest’uso della funzione forEach è banale ma rende bene l’idea. Il vantaggio principale è quello di rendere più snello e robusto il codice rispetto all’uso di un tradizionale ciclo for.

La funzione filter permette invece di filtrare i contenuti di un array. Il filtro va definito usando la sintassi delle callback, ad esempio

Ritorna solamente gli elementi dispari dell’array. Ciò significa che la variabile odd sarà un array che contiene solo gli elementi dispari filtrati dall’array array_2. Per verificarlo basta eseguire il seguente codice

Che mostra come l’array originale (cioè array_2) non viene modificato dal filtro.

map(), some() e reduce()

map(), some() e reduce()

Vediamo altre tre funzioni del paradigma ECMAScript che possiamo usare con Node. Js. La prima funzione si chiama map. Il nome è piuttosto infelice perché non aiuta a ricordare l’uso della funzione, che è semplicemente quello di applicare una funzione (o mappa) a tutti gli elementi del array. Un risultato di questo tipo sarebbe ottenibile mediante un normale ciclo for, ma l’uso della funzione map rende molto più leggibile e mantenibile il codice. Consideriamo ad esempio una funzione che calcoli il quadrato di ogni numero presente nell’array:

che fornisce come risultato i numeri 1,4, 9,16, 25. L’operazione è molto semplice ma il codice beneficia comunque del notevole miglioramento sintattico.

Consideriamo infine due funzioni che permettono di estrarre dei valori di sintesi da un array. Ciò significa che dato un array di 10,100 o 1000 elementi, applicando una funzione di sintesi usiamo un certo algoritmo per calcolare un unico valore rappresentativo di tutti gli elementi dell’array. Il caso più frequente è forse quello di calcolare il valore medio dei numeri di una sequenza, ma il concetto di valore di sintesi si applica a qualsiasi tipologia di dati. Per inciso anche un array contenente stringhe può essere trattato per ottenere un valore di sintesi (ad esempio un acronimo).

La prima funzione di questo tipo è molto semplice: la funzione some ritorna un valore di sintesi booleana, cioè vero o falso. Vediamo come usarla per capire se un certo array contiene almeno un numero pari:

se eseguiamo il codice qui sopra subito dopo quello delle pagine precedenti, ovvero dopo aver filtrato l’array odd in modo che contenga solo numeri dispari, vedremo che la funzione some ritornerà true nel primo caso e false nel secondo caso.

Molto più interessante è la funzione reduce. Essa permette di ridurre tutti gli elementi di un array ad un unico valore, trattandoli a coppie. Ciò significa che la funzione che passiamo come callback deve accettare due argomenti. La prima volta che verrà invocata essa riceverà il primo e secondo argomento, combinandoli assieme e memorizzando il risultato “dell’unione” nel primo argomento. Ciò significa che dopo il primo passaggio un array di 5 elementi verrà ridotto a quattro 4, perché i primi due sono stati “ridotti” o “uniti”. La riduzione è ricorsiva, nel senso che dopo aver ridotto il primo e secondo elemento la funzione prosegue, combinando il “nuovo” primo elemento con il secondo (che in principio era il terzo elemento del array). In altre parole dopo ogni riduzione tutti elementi dell’array vengono traslati verso sinistra, fino a che si ottiene un array con un solo valore: questo sarà il valore di sintesi finale. Vediamo un esempio pratico

in questo caso la funzione pair è la callback che passiamo come argomento della reduce. La funzione pair è abbastanza intelligente, perché decide se accopiare gli elementi su base numerica o come stringa. Eseguendola sul primo array avremo come risultato “abcde”, che è il valore di sintesi del array array_1 (concatenazione degli elementi). Eseguendola sul secondo array avremo invece come risultato “15”, che è il valore di sintesi del array array_2 (somma degli elementi).

Anche le altre funzionalità del linguaggio ECMAScript possono risultare utili, ma non vale la pena approfondirle in questa sede. La maggior parte delle funzionalità ECMAScript riguardano la programmazione ad oggetti. L’importante è ricordarsi che la sintassi Node.js è generalmente più ampia di quella JavaScript: il consiglio è di memorizzare un link alle specifiche ECMAScript tra i propri preferiti e ricordarsi di controllarlo tutte le volte che consultiamo la documentazione delle API JavaScript. In questo modo potremo sfruttare appieno le specifiche ECMAScript che non risultano elencate tra le API JavaScript.