In JavaScript, una funzione non è una “struttura magica del linguaggio”, ma un tipo speciale di valore.
La sintassi che abbiamo usato prima è chiamata Dichiarazione di funzione:
function sayHi() { alert( "Hello" );}
C’è un’altra sintassi per creare una funzione che è chiamata Espressione di funzione.
Sembra così:
let sayHi = function() { alert( "Hello" );};
Qui, la funzione è creata e assegnata alla variabile esplicitamente, come qualsiasi altro valore. Non importa come la funzione è definita, è solo un valore memorizzato nella variabile sayHi
.
Il significato di questi esempi di codice è lo stesso: “creare una funzione e metterla nella variabile sayHi
“.
Possiamo anche stampare quel valore usando alert
:
function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code
Si noti che l’ultima linea non esegue la funzione, perché non ci sono parentesi dopo sayHi
. Ci sono linguaggi di programmazione in cui qualsiasi menzione di un nome di funzione provoca la sua esecuzione, ma JavaScript non è così.
In JavaScript, una funzione è un valore, quindi possiamo trattarla come un valore. Il codice sopra mostra la sua rappresentazione in stringa, che è il codice sorgente.
Certo, una funzione è un valore speciale, nel senso che possiamo chiamarla come sayHi()
.
Ma è ancora un valore. Quindi possiamo lavorare con esso come con altri tipi di valori.
Possiamo copiare una funzione in un’altra variabile:
function sayHi() { // (1) create alert( "Hello" );}let func = sayHi; // (2) copyfunc(); // Hello // (3) run the copy (it works)!sayHi(); // Hello // this still works too (why wouldn't it)
Ecco cosa succede sopra in dettaglio:
- La dichiarazione di funzione
(1)
crea la funzione e la mette nella variabile chiamatasayHi
. - La linea
(2)
la copia nella variabilefunc
. Notate ancora: non ci sono parentesi doposayHi
. Se ci fossero, allorafunc = sayHi()
scriverebbe il risultato della chiamatasayHi()
infunc
, non la funzionesayHi
stessa. - Ora la funzione può essere chiamata sia
sayHi()
chefunc()
.
Nota che avremmo anche potuto usare un’espressione di funzione per dichiarare sayHi
, nella prima riga:
let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...
Tutto funzionerebbe allo stesso modo.
Vi potreste chiedere, perché l’espressione di funzione ha un punto e virgola ;
alla fine, ma la dichiarazione di funzione no:
function sayHi() { // ...}let sayHi = function() { // ...};
La risposta è semplice:
- Non c’è bisogno di
;
alla fine dei blocchi di codice e delle strutture sintattiche che li usano comeif { ... }
,for { }
,function f { }
ecc. - Un’espressione di funzione è usata all’interno della dichiarazione:
let sayHi = ...;
, come valore. Non è un blocco di codice, ma piuttosto un’assegnazione. Il punto e virgola;
è raccomandato alla fine delle dichiarazioni, non importa quale sia il valore. Quindi il punto e virgola qui non è correlato all’espressione di funzione stessa, termina solo la dichiarazione.
Funzioni di callback
Guardiamo altri esempi di passaggio di funzioni come valori e di utilizzo delle espressioni di funzione.
Scriveremo una funzione ask(question, yes, no)
con tre parametri:
question
Testo della domanda yes
Funzione da eseguire se la risposta è “Sì” no
Funzione da eseguire se la risposta è “No”
La funzione dovrebbe chiedere il question
e, a seconda della risposta dell’utente, chiamare yes()
o no()
:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}function showOk() { alert( "You agreed." );}function showCancel() { alert( "You canceled the execution." );}// usage: functions showOk, showCancel are passed as arguments to askask("Do you agree?", showOk, showCancel);
In pratica, tali funzioni sono abbastanza utili. La differenza principale tra un ask
della vita reale e l’esempio precedente è che le funzioni della vita reale usano modi più complessi per interagire con l’utente rispetto a un semplice confirm
. Nel browser, tale funzione di solito disegna una bella finestra di domanda. Ma questa è un’altra storia.
Gli argomenti showOk
e showCancel
di ask
sono chiamati funzioni di callback o semplicemente callback.
L’idea è che passiamo una funzione e ci aspettiamo che venga “richiamata” in seguito, se necessario. Nel nostro caso, showOk
diventa il callback per la risposta “sì”, e showCancel
per la risposta “no”.
Possiamo usare le Function Expressions per scrivere la stessa funzione molto più breve:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); });
Qui le funzioni sono dichiarate proprio dentro la chiamata ask(...)
. Non hanno un nome, e quindi sono chiamate anonime. Tali funzioni non sono accessibili al di fuori di ask
(perché non sono assegnate a variabili), ma è proprio quello che vogliamo qui.
Questo codice appare nei nostri script molto naturalmente, è nello spirito di JavaScript.
Valori regolari come stringhe o numeri rappresentano i dati.
Una funzione può essere percepita come un’azione.
Possiamo passarla tra variabili ed eseguirla quando vogliamo.
Espressione di funzione vs Dichiarazione di funzione
Formuliamo le differenze chiave tra Dichiarazioni di funzione ed Espressioni.
Primo, la sintassi: come differenziarle nel codice.
-
Dichiarazione di funzione: una funzione, dichiarata come una dichiarazione separata, nel flusso principale del codice.
// Function Declarationfunction sum(a, b) { return a + b;}
-
Espressione di funzione: una funzione, creata dentro un’espressione o dentro un altro costrutto di sintassi. Qui, la funzione è creata alla destra dell'”espressione di assegnazione”
=
:// Function Expressionlet sum = function(a, b) { return a + b;};
La differenza più sottile è quando una funzione è creata dal motore JavaScript.
Una Function Expression è creata quando l’esecuzione la raggiunge ed è utilizzabile solo da quel momento.
Una volta che il flusso di esecuzione passa al lato destro dell’assegnazione let sum = function…
– ecco, la funzione è creata e può essere usata (assegnata, chiamata, ecc.) da ora in poi.
Le dichiarazioni di funzione sono diverse.
Una Dichiarazione di Funzione può essere chiamata prima che sia definita.
Per esempio, una Dichiarazione di Funzione globale è visibile in tutto lo script, non importa dove sia.
Questo è dovuto ad algoritmi interni. Quando JavaScript si prepara ad eseguire lo script, prima cerca le dichiarazioni di funzioni globali in esso e crea le funzioni. Possiamo pensarla come una “fase di inizializzazione”.
E dopo che tutte le Function Declarations sono state elaborate, il codice viene eseguito. Quindi ha accesso a queste funzioni.
Per esempio, questo funziona:
sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}
La Function Declaration sayHi
viene creata quando JavaScript si prepara ad avviare lo script ed è visibile ovunque in esso.
…Se fosse un’espressione di funzione, allora non funzionerebbe:
sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};
Le espressioni di funzione vengono create quando l’esecuzione le raggiunge. Questo accadrebbe solo nella linea (*)
. Troppo tardi.
Un’altra caratteristica speciale delle dichiarazioni di funzione è il loro ambito di blocco.
In modo rigoroso, quando una dichiarazione di funzione è all’interno di un blocco di codice, è visibile ovunque all’interno di quel blocco. Ma non al di fuori di esso.
Per esempio, immaginiamo di dover dichiarare una funzione welcome()
dipendente dalla variabile age
che otteniamo durante il runtime. E poi abbiamo intenzione di usarla qualche tempo dopo.
Se usiamo la Dichiarazione di funzione, non funzionerà come previsto:
let age = prompt("What is your age?", 18);// conditionally declare a functionif (age < 18) { function welcome() { alert("Hello!"); }} else { function welcome() { alert("Greetings!"); }}// ...use it laterwelcome(); // Error: welcome is not defined
Questo perché una Dichiarazione di funzione è visibile solo dentro il blocco di codice in cui risiede.
Ecco un altro esempio:
let age = 16; // take 16 as an exampleif (age < 18) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs)} else { function welcome() { alert("Greetings!"); }}// Here we're out of curly braces,// so we can not see Function Declarations made inside of them.welcome(); // Error: welcome is not defined
Come possiamo fare per rendere welcome
visibile fuori da if
?
L’approccio corretto sarebbe quello di usare un’espressione di funzione e assegnare welcome
alla variabile che è dichiarata fuori da if
e ha la visibilità adeguata.
Questo codice funziona come previsto:
let age = prompt("What is your age?", 18);let welcome;if (age < 18) { welcome = function() { alert("Hello!"); };} else { welcome = function() { alert("Greetings!"); };}welcome(); // ok now
O possiamo semplificarlo ulteriormente usando un operatore punto interrogativo ?
:
let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now
Come regola generale, quando abbiamo bisogno di dichiarare una funzione, la prima cosa da considerare è la sintassi Function Declaration. Dà più libertà nel modo di organizzare il nostro codice, perché possiamo chiamare tali funzioni prima che siano dichiarate.
Questo è anche meglio per la leggibilità, poiché è più facile cercare function f(…) {…}
nel codice che let f = function(…) {…};
. Le dichiarazioni di funzione sono più “accattivanti”.
…Ma se una dichiarazione di funzione non ci soddisfa per qualche motivo, o abbiamo bisogno di una dichiarazione condizionale (abbiamo appena visto un esempio), allora si dovrebbe usare l’espressione di funzione.
Sommario
- Le funzioni sono valori. Possono essere assegnati, copiati o dichiarati in qualsiasi punto del codice.
- Se la funzione è dichiarata come una dichiarazione separata nel flusso di codice principale, si chiama “Dichiarazione di funzione”.
- Se la funzione è creata come parte di un’espressione, si chiama “Espressione di funzione”.
- Le dichiarazioni di funzione sono elaborate prima che il blocco di codice sia eseguito. Sono visibili ovunque nel blocco.
- Le espressioni di funzione sono create quando il flusso di esecuzione le raggiunge.
Nella maggior parte dei casi in cui abbiamo bisogno di dichiarare una funzione, una Dichiarazione di funzione è preferibile, perché è visibile prima della dichiarazione stessa. Questo ci dà più flessibilità nell’organizzazione del codice, e di solito è più leggibile.
Quindi dovremmo usare un’Espressione di funzione solo quando una Dichiarazione di funzione non è adatta al compito. Abbiamo visto un paio di esempi di questo in questo capitolo, e ne vedremo altri in futuro.
Sì.