Difendersi dai parametri cattivi in Javascript

È da un po’ di tempo che sono alla disperata ricerca dell’ispirazione: di cosa potrei scrivere nel mio primo articolo su questo blog? Probabilmente qualcosa a proposito del linguaggio Javascript, su cui sto lavorando più o meno ininterrottamente da mesi, ma cosa?
Il web è inondato di sviluppatori che scrivono di nuove tecnologie, patterns, librerie e strumenti più o meno complessi, ma purtroppo spesso si tende a bistrattare la sintassi e la pulizia del codice.
Un “trucco” che mi aveva colpito la prima volta che me ne sono venuto a conoscenza è l’inizializzazione delle variabili con valori di default utilizzando gli operatori logici, vedremo quindi di cosa si tratta, come si potrebbe utilizzare questa tecnica e come non dovrebbe essere utilizzata.

Sul campo di battaglia come mamma ci ha fatto

Tutti sappiamo quanto – in questo linguaggio – sia importante cautelarsi controllando che i parametri passati alle nostre funzioni abbiano i valori che ci aspettiamo, e chi più chi meno tutti cerchiamo di avere un approccio difensivo.

var example1 = function(param1, param2, onComplete){
	//...
	
	onComplete();
};

Quando la nostra funzione sarà eseguita non potremo avere certezza che tutti i tre parametri gli saranno passati così come ci aspettiamo, né che questi saranno valorizzati. Essendo inoltre il Javascript un linguaggio a tipizzazione dinamica, non saremo nemmeno sicuri che i tipi delle variabili saranno quelli attesi.

Proteggersi è importante e non lo si fa mai abbastanza

var example2 = function(param1, param2, onComplete){
	param1 = param1 || "Lorem ipsum dolor...";
	param2 = param2 || false;
	onComplete = _.isFunction(onComplete) ? onComplete : function(){return;};

	//...

	onComplete();
};

Per i primi due parametri abbiamo usato l’operatore OR in cortocircuito, che in questo contesto forza l’assegnazione della variabile al secondo valore (quello dopo il ||) qualora il primo sia “falsy” (false, undefined, null, “”, 0, NaN).
Nel caso del parametro “onComplete” invece non ci siamo accontentati di assegnargli una funzione fittizia nel caso in cui questa variabile non sia stata impostata, ma abbiamo usato la comoda funzione isFunction della libreria underscore, in modo da avere la certezza che onComplete sia – alla fine – una funzione, così da evitare pericolosi errori a runtime.

Le scorciatoie possono condurre alla rovina

L’operatore logico OR in cortocircuito usato per parametri booleani può nascondere dei tranelli, e com’è lecito aspettarsi il più subdolo che mi è capitato di incontrare ha a che fare proprio con valori logici.

var example3 = function(booleanParam){
	booleanParam = booleanParam || true;
	//...
	return booleanParam;
};
console.log(example3(false));

Chi ha scritto questa funzione magari lo ha fatto pensando di assegnare true come valore di default per la funzione booleanParam, in modo da cautelarsi in caso di mancato passaggio di parametri o di arrivo di un null/undefined. La triste realtà è che seppure nelle nostre intenzioni booleanParam dovrebbe avere come valore solo true o false, con true come default, questa variabile sarà sempre valorizzata con true. Questo perché l’assegnazione viene fatta al secondo valore qualora il primo sia “falsy”, e false lo è. In questi casi diffidate quindi di queste scorciatoie e utilizzate tecniche più verbose ma anche più sicure, come ad esempio la funzione isBoolean della libreria underscore, già usata in precedenza.

Conclusioni

Tutti gli esempi mostrati finora soffrono di una debolezza strutturale: se dovessi avere necessità di modificare la firma della funzione aggiungendo dei parametri, rischierei di fare danni a causa del “poco supporto” (per usare un eufemismo) che il compilatore (assente) ci da in questi casi. Nel dubbio io ho cominciato a sfruttare gli object literals per il passaggio dei parametri, così da rendere le mie funzioni un po’ più facili da manutenere e modificare.

var example4 = function(params){
	params = params || {};
	params.param1 = params.param1 || "Lorem ipsum dolor...";
	params.param2 = params.param2 || false;
	params.onComplete = _.isFunction(params.onComplete) ? params.onComplete : function(){return;};

	//...
	
	params.onComplete();
};
example4({'param2' : true, 'param1' : 'string'});

Secondo la mia esperienza definendo le vostre funzioni in questo modo vi risparmierete alcuni grattacapi quando andrete a modificare la firma delle vostre funzioni, e state tranquilli che prima o poi succederà.
Purtroppo la chiamata della funzione risulterà un po’ più verbosa che non esplicitando tutti i parametri direttamente, ma se è soltanto questo il costo della flessibilità conviene pagare questo dazio e non pensarci più.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.