Node.js: come gestire i parametri opzionali

Lavorando con Node.js possiamo realizzare applicazioni interattive senza la necessita di conoscere un linguaggio di programmazione di pagine dinamiche. Sia per realizzare l'interfaccia, sia per gestire la persistenza dei dati degli utenti, possiamo usare la sintassi JavaScript. Considerando tale scenario di lavoro e importante sapere come definire le firme dei metodi delle funzioni JavaScript. In altre parole e bene che le nostre librerie siano scritte fornendo delle API robuste e usando una notazione standard.

Lavorando con Node.js possiamo realizzare applicazioni interattive senza la necessità di conoscere un linguaggio di programmazione di pagine dinamiche. Sia per realizzare l’interfaccia, sia per gestire la persistenza dei dati degli utenti, possiamo usare la sintassi JavaScript. Considerando tale scenario di lavoro è importante sapere come definire le firme dei metodi delle funzioni JavaScript. In altre parole è bene che le nostre librerie siano scritte fornendo delle API robuste e usando una notazione standard.

La scelta di usare la sintassi JavaScript per realizzare le librerie di Node.js presenta (apparentemente) qualche perplessità. JavaScript è volutamente poco rigido nella definizione delle variabili, soprattutto se lo confrontiamo con i normali linguaggi di programmazione server side. Usando JavaScript per scrivere le librerie di Node.js potremmo riscontrare difficoltà nella definizione della firma delle funzioni. Il problema principale solitamente riguarda la gestione dei parametri opzionali. Tutte le volte che la funzione prevede uno o più parametri opzionali rischiamo di scontrarci con alcune ambiguità tipiche di JavaScript. Questo articolo spiega come fare affinché nostre librerie Node.js non presentino ambiguità di questo tipo.

Consideriamo il seguente snippet

per toccare con mano la questione invochiamo la funzione test in diversi modi

Eseguendo queste chiamate vedremo che tutte le invocazioni della funzione test, tranne l’ultima, vengono riconosciute come invocazioni con un parametro valido. Solamente nel caso dell’ultima funzione la sintassi JavaScript permette di capire che non è stato passato alcun parametro. E’ proprio questa l’ambiguità di cui parlavamo prima: la notazione delle API comune alla maggior parte dei linguaggi di programmazione prevede che l’assenza di un parametro vada specificata tramite opportune keyword (ad esempio null oppure undefined). Non è invece corretto assumere che una stringa vuota (oppure una stringa contenente il valore “false”) venga interpretata come parametro non valorizzato. JavaScript non richiede l’uso di alcuna keyword, ma lascia allo sviluppatore la libertà di decidere quando considerare “nullo” un parametro: tale libertà potrebbe generare alcune ambiguità nell’utilizzo delle API. Nelle prossime pagine vedremo qual è il modo corretto di gestire la situazione.

Parametri opzionali

Parametri opzionali

Una soluzione frequente utilizza l’operatore logico OR (simbolo “||”) per distinguere i parametri valorizzati da quelli non valorizzati. Questa soluzione in realtà non risolve il problema, perché resta l’ambiguità tra un parametro non valorizzato e la stringa nulla. In altre parole, quando eseguiamo l’operazione OR con la stringa nulla il risultato dell’operazione non distingue tra i due casi. Per verificarlo basta eseguire il seguente codice

Entrambi i casi ritornano infatti come risultato il messaggio “No argument”. Una soluzione migliore sfrutta l’istruzione typeof, che permette di identificare il tipo di variabile. In associazione con l’operatore “===” (tre simboli “=”) ciò permette di riconoscere l’uguaglianza sia del valore della variabile, sia del tipo di variabile. Per approfondimenti sull’argomento rimandiamo a questo articolo.

La soluzione consigliata è la seguente

Eseguendo lo snippet qui sopra vedremo che con questo “ trucchetto” riusciamo a distinguere la stringa nulla da un parametro non valorizzato. I simboli “#” nell’istruzione di log servono unicamente ad enfatizzare il fatto che la variabile passata come argomento è effettivamente una stringa nulla (altrimenti potremmo non distinguerla da una serie di caratteri blank).

A questo punto il problema sembra completamente risolto. Esiste però un ulteriore complicazione: molto spesso alcune API di Node.js prevedono l’uso di una callback, la quale viene passata (per convenzione) dopo alcuni parametri opzionali. In questo caso, a seconda che i parametri opzionali vengano utilizzati o meno, la posizione della callback nella firma della funzione potrebbe cambiare. In questo caso dobbiamo fare un po’ d’attenzione per capire quale effettivamente dei tre parametri d’ingresso contiene la callback: approfondiamo la questione nella prossima pagina.

Gestione delle callback

Gestione delle callback

Consideriamo il seguente scenario: abbiamo una funzione Node.js che prende in ingresso tre parametri. Il primo parametro è sempre obbligatorio, il secondo parametro è opzionale, il terzo parametro è una callback obbligatoria. Ciò significa che la funzione può essere invocata con due o tre parametri, a seconda che l’utente valorizzi o meno il parametro “centrale”. Un esempio di funzione di questo tipo è la seguente

Dove la keyword opt identifica il parametro opzionale. Ciò significa che la funzione potrebbe essere invocata in questi modi

per semplicità, qui sopra abbiamo usato come callback una banale funzione che stampa il solito messaggio “Ciao mondo!”. Si potrebbe pensare di gestire la situazione come a pagina precedente, cioè usando gli operatori typeof e “===”. Ciò è corretto se prestiamo attenzione alla posizione del parametro che identifica la callback. Un modo di farlo è il seguente

Come si vede dal codice qui sopra la callback potrebbe andare a finire nella variabile (locale) opt oppure nella variabile (sempre locale) cb. In altre parole, se il parametro opzionale non viene precisato al momento dell’invocazione della funzione, tutti i parametri alla sua destra (in questo caso solo la callback) vengono traslati di un posto verso sinistra. Ecco perché, quando il parametro opzionale non viene precisato, per eseguire la callback dobbiamo invocare l’istruzione opt() anziché l’istruzione cb().

Quest’ultimo accorgimento, unito alla gestione della variabile arguments vista nel primo esempio, permette di definire delle API che siano robuste, più robuste ai bug e soprattutto scritte usando le consuetudini normalmente accettate. Questo è un aspetto da tenere in mente prima di iniziare a lavorare su un grosso progetto, perché la definizione delle API è particolarmente importante nell’architettura di Node.js. Le applicazioni Node.js sono spesso modulari, per questo motivo consigliamo di progettare e definire con molta cura le API delle diverse librerie, tenendo conto delle linee guida qui suggerite.