JavaScript è dunque un linguaggio "ad oggetti" basato sul concetto di prototipo a differenza della maggior parte dei linguaggi di programmazione basati sul concetto di classe. Questa sottile differenza ha spesso causato difficoltà agli sviluppatori abituati ad utilizzare linguaggi come Java o C#, dove un oggetto è sempre un'istanza di una classe. Alcune librerie implementano funzionalità che consentono di avvicinare l'approccio prototipale degli oggetti di JavaScript a quello basato sulle classi degli altri linguaggi di programmazione, naturalmente con le dovute differenze.
Differenze sostanziali tra classi e prototipi
Nei linguaggi di programmazione a oggetti basati su classi, un oggetto non può esistere se non è stata prima definita una classe. Una classe è un'astrazione che definisce le caratteristiche che avranno gli oggetti creati da essa. In JavaScript invece ogni oggetto è creato direttamente, senza bisogno di definire prima una classe.
Nei linguaggi a oggetti tradizionali è possibile creare una sottoclasse a partire da una classe e quindi oggetti che ereditano le caratteristiche della classe base. In JavaScript è possibile ottenere un effetto analogo tramite il concetto di prototipo, con il quale un oggetto prende a modello un altro oggetto condividendone le carattersitiche ed eventualmente aggiungendone di nuove.
A differenza dei linguaggi a oggetti tradizionali, dove una classe definisce tutte le caratteristiche che può avere un oggetto, in JavaScript è possibile definire alcune caratteristiche per un oggetto ed arricchirlo di nuove proprietà e metodi a a runtime.
Classi in JavaScript (Ecma 6)
Per cercare di semplificare l'avvicinamento alla programmazione in JavaScript da parte degli sviluppatori con esperienza in altri contesti di programmazione ad oggetti e soprattutto per uniformare i vari approcci proposti dalle varie librerie, a partire da ECMASCript 6 è prevista la possibilità di utilizzare un approccio sintattico alla definizione di oggetti analogo alle classi.
L'argomento è stato a lungo dibattuto tra i membri del comitato per la definizione delle specifiche dello standard fino ad arrivare ad un modello condiviso.
Una classe in questo modello sintattico è un modo alternativo per la definizione di un costruttore. Possiamo ad esempio definire il costruttore dell'oggetto persona
nel seguente modo:
class persona {
constructor(nome, cognome) {
this.nome = nome;
this.cognome = cognome;
this.email = "";
this.indirizzo = "";
}
mostraNomeCompleto() {
return this.nome + " " + this.cognome;
}
}
All'interno della classe definiamo il nostro costruttore tramite il nome di funzione riservato constructor
e i metodi del nostro oggetto.
Nonostante l'utilizzo della parola chiave class
è da tener presente che quello che stiamo definendo non è in realtà una vera classe, ma un costruttore semanticamente equivalente a quello che abbiamo creato in precedenza tramite la definizione di una function
. L'introduzione del costrutto class
è solo una semplificazione sintattica, ma semanticamente JavaScript rimane un linguaggio senza classi.
Possiamo creare un nuovo oggetto a partire dalla classe appena definita tramite l'operatore new
, in modo del tutto identico a come abbiamo fatto con il costruttore definito tramite funzione:
var marioRossi = new persona("Mario", "Rossi");
È possibile creare getter
e setter
di proprietà in modo abbastanza intuitivo come mostrato dal seguente codice:
class persona {
constructor(nome, cognome) {
this.nome = nome;
this.cognome = cognome;
this._email = "";
this.indirizzo = "";
}
mostraNomeCompleto() {
return this.nome + " " + this.cognome;
}
get email() { return this._email; }
set email(value) {
var emailRegExp = /\w+@\w+\.\w{2,4}/i;
if (emailRegExp.test(value)) {
this._email = value;
} else {
console.log("Email non valida!");
}
}
}
Sottoclassi e Ereditarietà
Possiamo creare sottoclassi estendendo una classe nel seguente modo:
class programmatore extends persona {
constructor(nome, cognome) {
super(nome, cognome);
this.linguaggiConosciuti = [];
}
}
La parola chiave extends consente di dichiarare che la nuova classe programmatore
deriva dalla classe persona
. All'interno del costruttore della classe programmatore
richiamiamo il costruttore della classe persona
tramite l'invocazione di super(). In generale possiamo fare riferimento alla superclasse o classe base proprio tramite la parola chiave super
.
Come possiamo vedere, l'introduzione delle classi in JavaScript semplifica la scrittura del codice e lo rende più accessibile a chi è abituato a lavorare con un modello di programmazione ad oggetti tradizionale. Ricordiamoci però che JavaScript non ha classi e che quello che definiamo con la parola chiave class
non è altro che un costruttore di oggetti.