31-32 funzioni espressioni
Nel js, le funzioni oltre ad essere considerate istruzioni statement come quelle viste fino ad oggi, possono essere trattate come dati, come espressioni, come risultati da passare come parametri ad altre funzioni o come da restituire come risultato di un’invocazione di altre funzioni. Questo ha dotato js delle caratteristiche del paradigma di programmazione funzionale che si unisce a quello imperativo o procedurale che abbiamo utilizzato finora, incentrato sul concetto di istruzione che modifica lo stato di un qualcosa (variabile, stringa, oggetto….) . Inoltre ai due si somma anche il paradigma OOP Object Oriented Programming . Questi 3 aspetti procedurali fanno di js multiparadigma . La forza di js è di averli tutti disponibili, per utilizzare quello che meglio si adatta alle esigenze.
var media = function (a, b) { return (a+b)/2 ; };
La funzione può essere memorizzata in una variabile, da cui il termine funzione espressione.
Non è necessario assegnare un nome alla funzione, funzione anonima e specificare i parametri e le istruzioni da eseguire. Da notare il punto e virgola alla fine per chiudere l’istanza della variabile media. Ovviamente per richiamare la funzione basta richiamare la variabile.
writeln( media(4,9) );
Ripassino sulla ricorsione
var fattoriale = function fatt(n) { if ( n===0 ) {return 1;} else {return n * fatt(n-1);} }
memorizzo nella variabile fattoriale la funzione fatt che calcola il fattoriale di n , il caso terminatore è quando siamo arrivato a zero if ( n===0 ), dove restituisco 1, altrimenti restituisco il fattoriale di n. Calcoliamo il fattoriale di 5
writeln( fattoriale(5) );
Vediamo un altro esempio
var indiceMercato = 101;
immaginate in realtá un calcolo complesso che restituisca l’indice di mercato
if (indiceMercato<100) { function calcolaRischio(valoreInvestimento) { //anche qui un calcolo molto complesso ... return 1000; } }
se l’indice è minore di 100 applico la funzione calcolaRischio, altrimenti applico quest’altra
else { function calcolaRischio(valoreInvestimento) { //e anche qui ma completamente diverso dal precedente ... return 500; } }
Tutta questa logica di programmazione è attualmente un errore procedurale, l’interprete del linguaggio VIETA di racchiudere funzioni all’interno di blocchi if / else. Al lato pratico i browser tollerano tale sintassi, ma potrei ottenere dei risultati non voluti a seconda dei casi.
La soluzioni è passare da dichiarazioni di funzioni a FUNZIONI ESPRESSIONE .
var calcolaRischio; if (indiceMercato<100) { calcolaRischio = function (valoreInvestimento) { //anche qui un calcolo molto complesso ... return 1000; } } else { calcolaRischio = function (valoreInvestimento) { //e anche qui ma completamente diverso dal precedente ... return 500; } } writeln( calcolaRischio(40000) );
In questo caso non ho il rischio che la funzione possa essere richiamata dall’esterno del blocco e la variabile verrà eseguita solo nell’if o nell’else eliminando ogni ambiguità.
Hoisting (approfondimento)
Chi non ha pratica col meccanismo di parsing di js potrebbe sbagliare logica sul codice seguente:
function esterna() { function interna() { return "interna1"; } return interna(); function interna() { return "interna2"; } }
writeln ( esterna() );
Si richiede di stampare il valore restituito dalla funzione esterna: writeln ( esterna() );
Sono perfettamente lecite le funzioni annidate , quindi dentro la funzione esterna viene dichiarate la funzione interna che restituisce la stringa interna1.
Essendoci dopo il return interna() un altra funzione interna, ci si aspetta che prima venga stampata la stringa interna1, invece viene stampato interna2. Quando l’interprete incontra il nostro script, ne farà, prima di eseguirlo, la scansione completa , costruendosi un elenco completo con tutti gli identificatori di variabile , di funzioni e altri oggetti, il tutto prima di eseguire qualsiasi linea di istruzione, come il return .
Quando verrà scansionato il nostro codice il secondo valore della funzione interna, sovrascriverà il primo e il return restituirà il valore in memoria (il secondo).
Tutta questa prescansione totale, dall’inizio alla fine, per portare in evidenza tutti gli oggetti e le dichiarazioni è chiamata hoisting .
Cambiando lo script con funzioni espressione:
function esterna2() { var interna = function() { return "interna1"; } return interna(); var interna = function() { return "interna2"; } }
l’hoisting scansionerà tutte le dichiarazioni, ma non le istruzioni.
writeln ( esterna2() );
a questo punto verrà eseguito la funzione e dichiarata la variabile interna alla funzione ed il return andrà a lavorare con la definizione attiva in quel momento quindi stamperà interna1
Un altro esempio che dimostra che le funzioni espressione non sono interessate dall’hoisting:
function esterna2() { return interna(); var interna = function() { return "interna1"; } var interna = function() { return "interna2"; } }
In questo caso abbiamo spostato return all’inizio della funzione e il codice genererà un errore perché in quel punto la variabile interna non è stata ancora dichiarata.
Nella versione precedente, quella con le dichiarazioni scansione prima, spostando il return all’inizio, l’errore non appare, perché la dichiarazione della funzione è presente già nell’ hoisting.
Adesso capiamo perché l’utilizzo della funzione nell’if/else, nel caso del calcolaRischio, non è corretto, un browser potrebbe generare un hoisting differente da un altro, con conseguente valore differente e inaspettato.