FIRST CLASS FUNCTION
In questo post parleremo di quelle che sono le caratteristiche fondamentali delle funzioni in Javascript. Ti illustrerò concetti molto importanti come l’astrazione, l’incapsulamento che si usava in ES5 chiamato IIFE, la parola chiave this etc.
Traducendo questa definizione per quanto riguarda le funzioni JS vuol dire che tutto quello che possiamo fare con stringhe numeri etc. possiamo farlo con le funzioni. Così come possiamo assegnare una variabile ad un numero, una stringa etc. così possiamo assegnare quella variabile a una funzione. Vediamo un esempio di codice per chiarire questi concetti.
FUNZIONI E ASTRAZIONE
Vediamo cosa si intende per astrazione.
DEFINIZIONE
Degli esempi concreti ci verranno in aiuto.
IIFE E MODULE PATTERN
CONSTRUCTOR FUNCTION
LA KEYWORD THIS
FUNZIONI PURE E IMPURE
Le funzioni pure e impure sono un concetto fondamentale nella programmazione procedurale. Una funzione che prende parametri in input e restituisce sempre lo stesso output è chiamata funzione deterministica.
Essendoci il console.log non è ancora una funzione pura in quanto può produrre effetti collaterali nel mondo esterno. Una funzione per essere pura non deve produrre effetti collaterali. console.log mostra a video un’informazione e questo è un effetto collaterale. Una funzione pura semplifica la manutenzione del codice e il debug.
APPROFONDIMENTI
Le funzioni JavaScript sono definite con la parola chiave function. È possibile utilizzare una dichiarazione di funzione o un’espressione di funzione.
DICHIARAZIONE DI FUNZIONE
In precedenza, in questo tutorial, hai appreso che le funzioni sono dichiarate con la seguente sintassi:
function functionName(parameters) {
// code to be executed
}
Le funzioni dichiarate non vengono eseguite immediatamente. Sono “salvate per un uso successivo” e verranno eseguite in seguito, quando vengono invocate (chiamate).
function myFunction(a, b) {
return a * b;
}
I punti e virgola vengono utilizzati per separare le istruzioni JavaScript eseguibili. Poiché una dichiarazione di funzione non è un’istruzione eseguibile, non è comune terminarla con un punto e virgola.
ESPRESSIONI DI FUNZIONI
Una funzione JavaScript può anche essere definita utilizzando un’espressione. Un’espressione di funzione può essere memorizzata in una variabile:
const x = function (a, b) {return a * b};
Dopo che un’espressione di funzione è stata memorizzata in una variabile, la variabile può essere utilizzata come una funzione:
const x = function (a, b) {return a * b};
let z = x(4, 3); //output 12
La funzione sopra è in realtà una funzione anonima (una funzione senza nome). Le funzioni memorizzate nelle variabili non necessitano di nomi di funzioni. Vengono sempre richiamate (chiamate) utilizzando il nome della variabile. La funzione sopra termina con un punto e virgola perché fa parte di un’istruzione eseguibile.
IL COSTRUTTORE FUNCTION()
Come hai visto negli esempi precedenti, le funzioni JavaScript sono definite con la parola chiave function. Le funzioni possono anche essere definite con un costruttore di funzioni JavaScript integrato chiamato Function().
const myFunction = new Function(“a”, “b”, “return a * b”);
let x = myFunction(4, 3); //output 12
In realtà non è necessario utilizzare il costruttore di funzioni. L’esempio sopra è lo stesso di scrivere:
const myFunction = function (a, b) {return a * b};
let x = myFunction(4, 3);
HOISTING DI FUNZIONI
Il sollevamento (hoisting) è il comportamento predefinito di JavaScript per spostare le dichiarazioni all’inizio dell’ambito corrente. Il sollevamento si applica alle dichiarazioni di variabili e alle dichiarazioni di funzioni. Per questo motivo, le funzioni JavaScript possono essere chiamate prima di essere dichiarate:
myFunction(5);
function myFunction(y) {
return y * y;
}
FUNZIONI AUTOINVOCANTI
Le espressioni di funzione possono essere “autoinvocate”. Un’espressione che si richiama automaticamente viene invocata (avviata) automaticamente, senza essere chiamata. Le espressioni di funzione verranno eseguite automaticamente se l’espressione è seguita da (). Non è possibile richiamare automaticamente una dichiarazione di funzione. Devi aggiungere parentesi attorno alla funzione per indicare che si tratta di un’espressione di funzione:
(function () {
let x = “Hello!!”; // I will invoke myself
})();
LE FUNZIONI POSSONO ESSERE UTILIZZATE COME VALORI
function myFunction(a, b) {
return a * b;
}
let x = myFunction(4, 3); // x=12
Le funzioni JavaScript possono essere utilizzate nelle espressioni:
function myFunction(a, b) {
return a * b;
}
let x = myFunction(4, 3) * 2; // x=24
LE FUNZIONI SONO OGGETTI
L’operatore typeof in JavaScript restituisce “function” per le funzioni. Tuttavia, le funzioni JavaScript possono essere meglio descritte come oggetti. Le funzioni JavaScript hanno sia proprietà che metodi. La proprietà arguments.length restituisce il numero di argomenti ricevuti quando è stata richiamata la funzione:
function myFunction(a, b) {
return arguments.length;
}
Il metodo toString() restituisce la funzione come stringa:
function myFunction(a, b) {
return a * b;
}
let text = myFunction.toString();
//return function myFunction(a, b) { return a * b; }
APPROFONDIMENTO AI
In JavaScript, le funzioni costruttrici, il concetto di prototipo degli oggetti, la parola chiave new, e la keyword this sono concetti fondamentali per la creazione e la gestione degli oggetti. Vediamo ciascuno di questi concetti in dettaglio:
1. Funzione Costruttrice
Una funzione costruttrice è un tipo speciale di funzione che viene utilizzata per creare nuovi oggetti. Quando una funzione viene utilizzata come costruttore, crea un nuovo oggetto e lo imposta come il contesto (this) all’interno della funzione. Le funzioni costruttrici in JavaScript iniziano solitamente con una lettera maiuscola, per convenzione, per differenziarle dalle normali funzioni.
Esempio di funzione costruttrice:
function Persona(nome, cognome) {
this.nome = nome;
this.cognome = cognome;
}
const persona1 = new Persona(‘Mario‘, ‘Rossi‘);
console.log(persona1.nome); // Output: Mario
In questo esempio, la funzione Persona agisce come costruttore. Utilizzando la parola chiave new, si crea un nuovo oggetto persona1, con le proprietà nome e cognome.
2. Prototipo degli Oggetti
Ogni oggetto in JavaScript ha una proprietà chiamata prototype che consente di condividere metodi e proprietà tra tutte le istanze create da una funzione costruttrice. Questo meccanismo è parte integrante del modello basato sui prototipi di JavaScript. Gli oggetti creati da una funzione costruttrice possono ereditare le proprietà e i metodi definiti nel prototipo dell’oggetto.
Esempio:
Persona.prototype.saluta = function() {
console.log(`Ciao, sono ${this.nome} ${this.cognome}`);
};
persona1.saluta(); // Output: Ciao, sono Mario Rossi
In questo esempio, il metodo saluta è stato aggiunto al prototipo della funzione costruttrice Persona. Ora tutte le istanze della funzione costruttrice avranno accesso a questo metodo.
3. La parola chiave new
La parola chiave new viene utilizzata per creare una nuova istanza di un oggetto che utilizza una funzione costruttrice. Quando si utilizza new:
•Viene creato un nuovo oggetto vuoto.
•Il contesto (this) all’interno della funzione costruttrice viene assegnato al nuovo oggetto creato.
•Se la funzione non restituisce esplicitamente un oggetto, il nuovo oggetto creato viene restituito automaticamente.
Esempio:
const persona2 = new Persona(‘Luigi‘, ‘Verdi‘);
console.log(persona2.nome); // Output: Luigi
4. La keyword this
La keyword this in JavaScript si riferisce al contesto in cui una funzione è chiamata. Quando this è utilizzato all’interno di una funzione costruttrice, si riferisce all’oggetto appena creato. Il valore di this può cambiare in base a come viene chiamata la funzione.
Esempio:
function MostraNome() {
console.log(this.nome);
}
const persona3 = new Persona(‘Franco‘, ‘Bianchi‘);
MostraNome.call(persona3); // Output: Franco
In questo esempio, il metodo call() imposta il contesto di this in MostraNome sull’oggetto persona3, permettendo di accedere alla proprietà nome.
Riepilogo:
•Funzione Costruttrice: Una funzione utilizzata per creare nuovi oggetti, che viene invocata con la parola chiave new.
•Prototipo degli Oggetti: Un meccanismo che permette di condividere metodi e proprietà tra tutte le istanze di un oggetto.
•Parola chiave new: Crea un nuovo oggetto basato su una funzione costruttrice.
•Keyword this: Riferisce all’oggetto attuale o al contesto in cui una funzione viene chiamata.
In JavaScript, le funzioni sono considerate cittadini di prima classe (o “first-class citizens”) e possono essere utilizzate come strumenti di astrazione. Questo concetto è fondamentale per comprendere la flessibilità e la potenza del linguaggio JavaScript. Vediamo entrambi i concetti in dettaglio:
Funzioni come Cittadini di Prima Classe (First-Class Citizens)
In molti linguaggi di programmazione, incluse le versioni più moderne di JavaScript, le funzioni sono trattate come oggetti di prima classe. Questo significa che le funzioni possono essere:
1. Assegnate a una variabile,
2. Passate come argomenti a un’altra funzione,
3. Restituite da un’altra funzione,
4. Salvate in strutture dati come array o oggetti,
5. Essere proprietà di un oggetto (metodi).
Essere cittadini di prima classe significa che le funzioni in JavaScript sono trattate come qualsiasi altro valore (come stringhe, numeri, oggetti, ecc.), e possono essere manipolate dinamicamente durante l’esecuzione.
Esempio:
// Assegnazione di una funzione a una variabile
const saluto = function(nome) {
return `Ciao, ${nome}!`;
};
console.log(saluto(“Marco“)); // Output: Ciao, Marco!
// Passare una funzione come argomento a un’altra funzione
function eseguiFunzione(f) {
return f(“Sara“);
}
console.log(eseguiFunzione(saluto)); // Output: Ciao, Sara!
In questo esempio, la funzione saluto viene assegnata a una variabile come se fosse un qualsiasi altro valore. Successivamente, viene passata come argomento a un’altra funzione, eseguiFunzione, che la esegue.
Implicazioni del Concetto di Prima Classe
Grazie a questo concetto, le funzioni possono essere utilizzate per costruire potenti astrazioni. Le astrazioni consentono di nascondere i dettagli di implementazione e di concentrarsi su una logica più generale.
1. Funzioni di Callback
Le funzioni di callback sono un esempio comune dell’uso di funzioni come cittadini di prima classe. Una funzione di callback è una funzione passata come argomento a un’altra funzione, che poi la “richiama” (ovvero la esegue) quando è necessario.
Esempio:
function operazione(matematica, a, b) {
return matematica(a, b);
}
function somma(x, y) {
return x + y;
}
console.log(operazione(somma, 3, 4)); // Output: 7
In questo esempio, somma è una funzione di callback passata a operazione, che la esegue con i parametri a e b. Questo è un esempio semplice, ma il concetto può essere usato per implementare logiche più complesse come la gestione di eventi o l’uso di funzioni asincrone.
2. Funzioni Anonime e Funzioni Freccia
Un’altra caratteristica di JavaScript è la possibilità di definire funzioni anonime, cioè funzioni senza un nome. Sono molto utili quando si vuole passare una funzione come argomento senza dichiararla esplicitamente.
Esempio:
setTimeout(function() {
console.log(“Eseguiamo questa funzione dopo 2 secondi“);
}, 2000);
Qui, una funzione anonima viene passata come callback a setTimeout, che la eseguirà dopo un certo periodo di tempo.
Le funzioni freccia sono una versione più compatta e moderna delle funzioni anonime:
setTimeout(() => {
console.log(“Eseguiamo questa funzione dopo 2 secondi“);
}, 2000);
Funzioni come Astrazioni
Le funzioni in JavaScript sono strumenti di astrazione, poiché ci permettono di definire un comportamento generico che può essere riutilizzato in diversi contesti. L’astrazione è il processo di nascondere la complessità e di concentrarsi sui concetti fondamentali, consentendo una programmazione più modulare, leggibile e manutenibile.
1. Funzioni di Ordine Superiore (Higher-Order Functions)
Le funzioni di ordine superiore sono funzioni che accettano altre funzioni come argomenti o che restituiscono una funzione come risultato. Questo approccio è potente perché permette di creare soluzioni più astratte e flessibili.
Esempio:
function creaMoltiplicatore(fattore) {
return function(numero) {
return numero * fattore;
};
}
const moltiplicaPerDue = creaMoltiplicatore(2);
console.log(moltiplicaPerDue(5)); // Output: 10
In questo esempio, creaMoltiplicatore è una funzione di ordine superiore che restituisce una nuova funzione. La funzione risultante viene utilizzata per moltiplicare un numero per il fattore fornito come argomento alla funzione principale.
2. Chiusure (Closures)
Le funzioni in JavaScript, grazie al loro comportamento di chiusura (closure), possono ricordare l’ambiente in cui sono state create, consentendo loro di accedere a variabili che non sono più presenti nello scope corrente.
Esempio:
function creaContatore() {
let contatore = 0;
return function() {
contatore++;
return contatore;
};
}
const incrementa = creaContatore();
console.log(incrementa()); // Output: 1
console.log(incrementa()); // Output: 2
In questo esempio, la funzione restituita dalla funzione creaContatore “chiude” sul contesto della variabile contatore, permettendole di mantenere il suo valore anche dopo che creaContatore ha terminato l’esecuzione.
Vantaggi delle Astrazioni con le Funzioni
•Modularità: Le funzioni astratte permettono di scomporre problemi complessi in componenti più piccoli e gestibili.
•Riutilizzabilità: Una funzione astratta può essere riutilizzata in diversi contesti con parametri diversi.
•Manutenibilità: La separazione delle preoccupazioni tramite funzioni rende il codice più facile da comprendere e mantenere.
Conclusione
Le funzioni come cittadini di prima classe in JavaScript consentono una flessibilità notevole nella scrittura del codice, permettendo di utilizzarle come qualsiasi altro valore. Questo, insieme alla loro capacità di fungere da astrazioni, permette di scrivere codice più modulare, riutilizzabile e manutenibile. L’uso delle funzioni come callback, funzioni di ordine superiore e chiusure è una parte essenziale di un approccio di programmazione funzionale, ampiamente supportato in JavaScript. In JavaScript, le funzioni possono essere classificate come pure o impure in base al loro comportamento e all’effetto che hanno sullo stato esterno. Ecco una spiegazione di entrambe:
Funzioni Pure:
Una funzione pura è una funzione che:
1. Dipende solo dai propri argomenti: il risultato della funzione dipende esclusivamente dai parametri che riceve, senza fare affidamento su variabili esterne o stati globali.
2. Non ha effetti collaterali: non modifica lo stato esterno alla funzione (ad esempio, non modifica variabili globali, non fa I/O, non muta l’input ricevuto).
3. Deterministica: a parità di input, restituirà sempre lo stesso output.
Esempio di funzione pura:
function somma(a, b) {
return a + b;
}
In questo caso, la funzione somma è pura perché:
•Dipende solo dai parametri a e b.
•Non modifica variabili esterne.
•Restituirà sempre lo stesso risultato per gli stessi valori di input.
Funzioni Impure:
Una funzione impura è una funzione che:
1. Fa affidamento su stati esterni o variabili globali: il risultato può dipendere da variabili esterne alla funzione o da stati modificabili durante l’esecuzione del programma.
2. Ha effetti collaterali: può modificare variabili esterne, fare operazioni di I/O (come interagire con il DOM o inviare richieste HTTP), o cambiare lo stato globale.
3. Non deterministica: può restituire risultati diversi anche con gli stessi input, se lo stato esterno cambia.
Esempio di funzione impura:
let x = 10;
function sommaImpura(a) {
return a + x;
}
In questo caso, la funzione sommaImpura è impura perché:
•Il risultato dipende dalla variabile esterna x.
•Se x cambia, la funzione restituirà un risultato diverso anche se a rimane lo stesso.
Perché le funzioni pure sono utili?
Le funzioni pure sono facili da testare, prevedibili e più facili da parallelizzare poiché non dipendono da stati esterni e non hanno effetti collaterali. Le funzioni impure possono essere necessarie in alcune situazioni, come interagire con l’utente o aggiornare il DOM, ma devono essere usate con attenzione.
Scrivi un commento