034.3 Lezione 2
Certificazione: |
Web Development Essentials |
---|---|
Versione: |
1.0 |
Argomento: |
034 Programmazione JavaScript |
Obiettivo: |
034.3 Strutture di Controllo e Funzioni in JavaScript |
Lezione: |
2 di 2 |
Introduzione
Oltre all’insieme standard di funzioni integrate fornite dal linguaggio JavaScript, gli sviluppatori possono scrivere le proprie funzioni personalizzate per mappare un input su un output adatto alle esigenze dell’applicazione. Le funzioni personalizzate sono fondamentalmente un insieme di istruzioni incapsulate per essere usate altrove come parte di un’espressione.
Usare le funzioni è un buon modo per evitare di scrivere codice duplicato, perché esse possono essere chiamate da diversi punti del programma. Inoltre, raggruppare le istruzioni in funzioni facilita il collegamento delle azioni personalizzate agli eventi, che è un aspetto centrale della programmazione JavaScript.
Definire una Funzione
Man mano che un programma cresce diventa più difficile organizzare ciò che fa senza usare funzioni. Ogni funzione ha il suo ambito “privato” di variabili, così le variabili definite all’interno di una funzione saranno disponibili solo all’interno di quella stessa funzione. Così facendo si ridurranno i rischi che si faccia confusione con le variabili di altre funzioni. Le variabili globali sono ancora accessibili dall’interno delle funzioni, ma il modo preferibile per inviare valori di input a una funzione è attraverso i parametri di funzione. Come esempio, ci baseremo sul validatore di numeri primi della lezione precedente:
// A naive prime number tester
// The number we want to evaluate
let candidate = 231;
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Display the result in the console window
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
Se più avanti nel codice hai bisogno di controllare se un numero è primo, sarebbe necessario ripetere il codice che è già stato scritto. Questa pratica non è raccomandata, perché qualsiasi correzione o miglioramento al codice originale avrebbe bisogno di essere replicato manualmente ovunque il codice sia stato copiato. Inoltre, ripetere il codice grava sul browser e sulla rete, possibilmente rallentando la visualizzazione della pagina web. Invece di fare questo, spostate le dichiarazioni appropriate in una funzione:
// A naive prime number tester function
function test_prime(candidate)
{
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Send the answer back
return is_prime;
}
La dichiarazione di funzione inizia con un’istruzione function
, seguita dal nome della funzione e dai suoi parametri. Il nome della funzione deve seguire le stesse regole dei nomi delle variabili. I parametri della funzione, noti anche come argomenti della funzione, sono separati da virgole e racchiusi da parentesi.
Tip
|
Elencare gli argomenti nella dichiarazione della funzione non è obbligatorio. Gli argomenti passati a una funzione possono essere recuperati da un oggetto |
Nell’esempio la funzione test_prime
ha un solo argomento: l’argomento candidate
, che è il candidato numero primo da testare. Gli argomenti delle funzioni operano come variabili, ma i loro valori sono assegnati dall’istruzione che chiama la funzione. Per esempio, l’istruzione test_prime(231)
richiamerà la funzione test_prime
e assegnerà il valore 231 all’argomento candidate
, che sarà poi disponibile nel corpo della funzione come una normale variabile.
Se l’istruzione chiamante usa variabili semplici per i parametri della funzione, i loro valori saranno copiati negli argomenti della funzione. Questa procedura — copiare i valori dei parametri usati nell’istruzione chiamante nei parametri usati all’interno della funzione — è chiamata passare gli argomenti per valore. Qualsiasi modifica apportata dalla funzione all’argomento non influisce sulla variabile originale usata nell’istruzione chiamante. Tuttavia, se l’istruzione chiamante usa oggetti complessi come argomenti (cioè un oggetto con proprietà e metodi collegati a esso) per i parametri della funzione, essi saranno passati come riferimento e la funzione potrà modificare l’oggetto originale usato nell’istruzione chiamante.
Gli argomenti che sono passati per valore, così come le variabili dichiarate all’interno della funzione, non sono visibili al di fuori di essa. Cioè, il loro ambito è limitato al corpo della funzione in cui sono stati dichiarati. Ciononostante, le funzioni sono solitamente impiegate per creare qualche output visibile al di fuori della funzione. Per condividere un valore con la sua funzione chiamante, una funzione definisce una dichiarazione return
.
Per esempio, la funzione test_prime
nell’esempio precedente restituisce il valore della variabile is_prime
. Pertanto, la funzione può sostituire la variabile ovunque sarebbe usata nell’esempio originale:
// The number we want to evaluate
let candidate = 231;
// Display the result in the console window
if ( test_prime(candidate) )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
L’istruzione return
, come indica il suo nome, restituisce il controllo alla funzione chiamante. Pertanto, ovunque l’istruzione return
sia posizionata nella funzione, nulla di ciò che la segue viene eseguito. Una funzione può contenere più dichiarazioni return
. Questa pratica può essere utile se alcune sono all’interno di blocchi condizionali di istruzioni, in modo che la funzione possa o meno eseguire una particolare istruzione return
a ogni esecuzione.
Alcune funzioni possono non restituire un valore, quindi la dichiarazione return
non è obbligatoria. Le istruzioni interne della funzione vengono eseguite indipendentemente dalla sua presenza, quindi le funzioni, per esempio, possono anche essere usate per cambiare i valori delle variabili globali o il contenuto degli oggetti passati per riferimento. Nonostante ciò, se la funzione non ha una dichiarazione return
, il suo valore di ritorno di default è impostato a undefined
: una variabile riservata che non ha un valore e non può essere scritta.
Espressioni di Funzione
In JavaScript, le funzioni sono solo un altro tipo di oggetto. Così, le funzioni possono essere impiegate in uno script come variabili. Questa caratteristica diventa esplicita quando la funzione è dichiarata usando una sintassi alternativa, chiamata espressione di funzione:
let test_prime = function(candidate)
{
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Send the answer back
return is_prime;
}
L’unica differenza tra questo esempio e la dichiarazione di funzione nell’esempio precedente è nella prima riga: let test_prime = function(candidate)
invece di function test_prime(candidate)
. In un’espressione di funzione, il nome test_prime
è usato per l’oggetto che contiene la funzione e non per nominare la funzione stessa. Le funzioni definite nelle espressioni di funzione sono chiamate allo stesso modo delle funzioni definite usando la sintassi di dichiarazione. Tuttavia, mentre le funzioni dichiarate possono essere chiamate prima o dopo la loro dichiarazione, le espressioni di funzione possono essere chiamate solo dopo la loro inizializzazione. Come per le variabili, chiamare una funzione definita in un’espressione prima della sua inizializzazione causerà un errore di riferimento.
Ricorsione di Funzione
Oltre a eseguire dichiarazioni e chiamare funzioni integrate, le funzioni personalizzate possono anche chiamare altre funzioni personalizzate, comprese se stesse. Chiamare una funzione da se stessa è chiamata ricorsione di funzione. A seconda del tipo di problema che state cercando di risolvere, l’uso di funzioni ricorsive può essere più semplice dell’uso di cicli annidati per eseguire compiti ripetitivi.
Finora sappiamo come usare una funzione per verificare se un dato numero è primo. Ora supponiamo che vogliate trovare il primo successivo a un dato numero. Potreste utilizzare un ciclo while
per incrementare il numero candidato e scrivere un ciclo annidato che cercherà i fattori interi per quel candidato:
// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
// We are only interested in the positive primes,
// so we will consider the number 2 as the next
// prime after any number less than two.
if ( from < 2 )
{
return 2;
}
// The number 2 is the only even positive prime,
// so it will be easier to treat it separately.
if ( from == 2 )
{
return 3;
}
// Decrement "from" if it is an even number
if ( from % 2 == 0 )
{
from--;
}
// Start searching for primes greater then 3.
// The prime candidate is the next odd number
let candidate = from + 2;
// "true" keeps the loop going until a prime is found
while ( true )
{
// Auxiliary control variable
let is_prime = true;
// "candidate" is an odd number, so the loop will
// try only the odd factors, starting with 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime.
// Test the next candidate
is_prime = false;
break;
}
}
// End loop and return candidate if it is prime
if ( is_prime )
{
return candidate;
}
// If prime not found yet, try the next odd number
candidate = candidate + 2;
}
}
let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));
Nota che abbiamo bisogno di usare una condizione costante per il ciclo while
(l’espressione true
dentro la parentesi) e la variabile ausiliaria is_prime
per sapere quando fermare il ciclo. Anche se questa soluzione è corretta, l’uso di loop annidati non è così elegante come l’uso della ricorsione per eseguire lo stesso compito:
// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
// We are only interested in the positive primes,
// so we will consider the number 2 as the next
// prime after any number less than two.
if ( from < 2 )
{
return 2;
}
// The number 2 is the only even positive prime,
// so it will be easier to treat it separately.
if ( from == 2 )
{
return 3;
}
// Decrement "from" if it is an even number
if ( from % 2 == 0 )
{
from--;
}
// Start searching for primes greater then 3.
// The prime candidate is the next odd number
let candidate = from + 2;
// "candidate" is an odd number, so the loop will
// try only the odd factors, starting with 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime.
// Call the next_prime function recursively, this time
// using the failed candidate as the argument.
return next_prime(candidate);
}
}
// "candidate" is not divisible by any integer factor other
// than 1 and itself, therefore it is a prime number.
return candidate;
}
let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));
Entrambe le versioni di next_prime
restituiscono il prossimo numero primo dopo il numero dato come unico argomento (from
). La versione ricorsiva, come quella precedente, inizia controllando i casi speciali (cioè i numeri minori o uguali a due). Poi incrementa il candidate
e inizia a cercare qualsiasi fattore intero con il ciclo for
(si noti che il ciclo while
non è più presente). A quel punto, l’unico numero primo pari è già stato testato, quindi il candidato e i suoi possibili fattori vengono incrementati di due (un numero dispari più due è il prossimo numero dispari).
Ci sono solo due modi per uscire dal ciclo for
dell’esempio. Se tutti i possibili fattori vengono testati e nessuno di essi ha un resto uguale a zero quando si divide il candidate
, il ciclo for
si completa e la funzione restituisce il candidato come il prossimo numero primo dopo from
. Altrimenti, se factor
è un fattore intero di candidate
(candidate % factor == 0
), il valore restituito viene dalla funzione next_prime
chiamata ricorsivamente, questa volta con il candidate
incrementato come parametro from
. Le chiamate per next_prime
saranno impilate l’una sull’altra, fino a quando un candidate
non troverà nessun fattore intero. Allora l’ultima istanza di next_prime
che contiene il numero primo lo restituirà alla precedente istanza di next_prime
, e quindi successivamente fino alla prima istanza di next_prime
. Anche se ogni invocazione della funzione usa gli stessi nomi per le variabili, le invocazioni sono isolate l’una dall’altra, quindi le loro variabili sono tenute separate in memoria.
Esercizi Guidati
-
Che tipo di sovraccarico può essere mitigato dagli sviluppatori usando le funzioni?
-
Qual è la differenza tra gli argomenti di funzione passati per valore e gli argomenti di funzione passati per riferimento?
-
Quale valore sarà usato come output di una funzione personalizzata se questa non ha una dichiarazione di ritorno?
Esercizi Esplorativi
-
Qual è la probabile causa di un Uncaught Reference Error registrato quando si chiama una funzione dichiarata con la sintassi expression?
-
Scrivi una funzione chiamata
multiples_of
che riceve tre argomenti:factor
,from
eto
. All’interno della funzione, usa l’istruzioneconsole.log()
per visualizzare tutti i multipli difactor
che si trovano trafrom
eto
.
Sommario
Questa lezione spiega come scrivere funzioni personalizzate nel codice JavaScript. Le funzioni personalizzate permettono allo sviluppatore di dividere l’applicazione in “pezzi” di codice riutilizzabile, rendendo più facile scrivere e mantenere programmi più grandi. La lezione passa in rassegna i seguenti concetti e procedure:
-
Come definire una funzione personalizzata: dichiarazioni di funzione ed espressioni di funzione.
-
Usare i parametri come input della funzione.
-
Usare l’istruzione
return
per impostare l’output della funzione. -
Ricorsione delle funzioni.
Risposte agli Esercizi Guidati
-
Che tipo di sovraccarico può essere mitigato dagli sviluppatori usando le funzioni?
Le funzioni ci permettono di riutilizzare il codice, il che facilita la sua manutenzione. Un file di script più piccolo fa anche risparmiare memoria e tempo di download.
-
Qual è la differenza tra gli argomenti di funzione passati per valore e gli argomenti di funzione passati per riferimento?
Quando viene passato per valore, l’argomento viene copiato nella funzione e la funzione non è in grado di modificare la variabile originale nell’istruzione chiamante. Quando viene passato per riferimento, la funzione è in grado di manipolare la variabile originale usata nell’istruzione chiamante.
-
Quale valore sarà usato come output di una funzione personalizzata se questa non ha una dichiarazione di ritorno?
Il valore restituito sarà impostato su
undefined
.
Risposte agli Esercizi Esplorativi
-
Qual è la probabile causa di un Uncaught Reference Error registrato quando si chiama una funzione dichiarata con la sintassi expression?
La funzione è stata chiamata prima della sua dichiarazione nel file di script.
-
Scrivi una funzione chiamata
multiples_of
che riceve tre argomenti:factor
,from
eto
. All’interno della funzione, usa l’istruzioneconsole.log()
per visualizzare tutti i multipli difactor
che si trovano trafrom
eto
.function multiples_of(factor, from, to) { for ( let number = from; number <= to; number++ ) { if ( number % factor == 0 ) { console.log(factor, "*", number / factor, "=", number); } } }