13. Classi

Qui presentiamo le classi ECMAScript 6. Innanzitutto, mostriamo che le funzioni possono già essere utilizzate come classi.
13.1. script [class-00]
Il seguente script mostra un uso insolito delle funzioni. Qui vengono utilizzate come oggetti.
'use strict';
// a function can be used as an object
// an empty shell
function f() {
}
// to which external properties are attributed
f.prop1 = "val1";
f.show = function () {
console.log(this.prop1);
}
// use of f
f.show();
// a function g operating as a class
function g() {
this.prop2 = "val2";
this.show = function () {
console.log(this.prop2);
}
}
// instantiate the function with [new]
new g().show();
Commenti
- righe 5–7: il corpo della funzione f non definisce alcuna proprietà;
- righe 9–12: le proprietà vengono assegnate alla funzione f dall’esterno;
- riga 14: uso della funzione (oggetto) f. Si noti che non scriviamo [f()] ma semplicemente [f]. Questa è la notazione per un oggetto;
- righe 17–22: definiamo una funzione [g] come se fosse una classe con proprietà e metodi;
- riga 24: la funzione [g] viene istanziata da [new g()];
Risultati dell'esecuzione
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-00.js"
val1
val2
ES6 ha introdotto il concetto di classi, che ora ci permette di evitare l'uso delle funzioni per creare classi.
13.2. script [class-01]
Lo script [class-01] presenta una classe [Person]:
// class
class Personne {
// manufacturer
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// getters and setters
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString to JSON
toString() {
return JSON.stringify(this);
}
}
// class call
function main() {
const personne = new Personne("Poirot", "Hercule", 66);
console.log("personne=", personne.toString(), typeof (personne), personne instanceof (Personne));
}
// hand call
main();
Commenti
- riga 2: la parola chiave [class] indica una classe;
- righe 5–9: la parola chiave [constructor] indica il costruttore della classe. Ne può esserci al massimo uno. Viene utilizzato per costruire e inizializzare un'istanza della classe. Si noti che non vi è alcuna dichiarazione delle proprietà [last_name, first_name, age];
- righe 11–36: proprietà della classe. Qui vediamo elementi già trattati nella sezione sugli oggetti 4, pagina 44. Cambia solo la sintassi;
- riga 41: creazione di un oggetto di tipo [Person]. D'ora in poi, l'oggetto [person] viene utilizzato come oggetto letterale. [typeof (person)] restituisce "object" e l'espressione [person instanceof (Person)] è vera. È quindi possibile determinare il tipo esatto di un'istanza di classe;
Risultati dell'esecuzione
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-01.js"
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66} object true
13.3. script [class-02]
Questo script mostra come ereditare da una classe usando la parola chiave [extends].
Per prima cosa, spostiamo la classe [Person] in un modulo chiamato [Person.js]:
// classe
class Personne {
// constructeur
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// getters et setters
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString en JSON
toString() {
return JSON.stringify(this);
}
}
// export classe
export default Personne;
- riga 39: esportiamo la classe [Person] in modo che gli script possano importarla;
Lo script [class-02] crea una classe [Teacher] derivata dalla classe [Person]:
// imports
import Personne from './Personne';
// classe
class Enseignant extends Personne {
// constructeur
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// getters et setters
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// appel de la classe
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// appel de main
main();
Commenti
- riga 2: importiamo la classe [Person] dal modulo [Person.js], che si trova nella stessa cartella di [class-02];
- riga 5: la classe [Teacher] estende (eredita da) la classe [Person] utilizzando la parola chiave [extends]: aggiunge una proprietà [_subject] con i corrispondenti metodi getter e setter;
- righe 8–11: il costruttore della classe [Teacher] riceve quattro valori per inizializzare le quattro proprietà della classe;
- riga 9: la parola chiave [super] chiama il costruttore della classe padre [Person], che inizializzerà quindi le proprietà [_last_name, _first_name, _age];
- riga 10: viene inizializzata la proprietà [_discipline] appartenente alla classe [Teacher];
- righe 14–19: i metodi getter e setter per la proprietà [_discipline];
- riga 25: viene creato un oggetto di tipo [Teacher];
- riga 26: utilizziamo il metodo [teacher.toString()]. La classe [Teacher] non dispone di questo metodo. Pertanto, viene automaticamente utilizzato il metodo della sua classe padre. Questo metodo rende l'espressione [JSON.stringify(this)], dove [this] sarà qui un oggetto [Teacher], non un oggetto [Person]. Questo è ciò che nella programmazione orientata agli oggetti è noto come polimorfismo di classe. Un termine complesso per JavaScript, che non è un linguaggio orientato agli oggetti. Tuttavia, JavaScript fa ciò che ci si aspetta da esso in questo caso: visualizza correttamente un insegnante;
I risultati dell'esecuzione sono i seguenti:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-02.js"
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
- riga 2: JavaScript riconosce correttamente che la variabile [teacher] è di tipo [Teacher];
13.4. script [class-03]
Lo script [class-03] mostra che una classe figlia può sovrascrivere proprietà e metodi della sua classe padre. Qui, sovrascriviamo il metodo [toString] della classe padre:
// imports
import Personne from './Personne';
// classe
class Enseignant extends Personne {
// constructeur
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// getters et setters
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
// redéfinition de toString
toString() {
return "[Enseignant]" + JSON.stringify(this);
}
}
// appel de la classe
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// appel de main
main();
I risultati dell'esecuzione sono i seguenti:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-03.js"
enseignant= [Enseignant]{"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
13.5. script [class-04]
Lo script [class-04] dimostra ancora una volta il polimorfismo in azione: dove una funzione si aspetta un parametro formale di tipo [Person], possiamo passare un tipo derivato come [Teacher]. Infatti, in virtù della derivazione, il tipo [Teacher] possiede tutti gli attributi del tipo [Person].
Per prima cosa, isoliamo il tipo [Teacher] in un modulo [Teacher.js]:
// imports
import Personne from './Personne';
// classe
class Enseignant extends Personne {
// constructeur
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// getters et setters
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// export classe
export default Enseignant;
- riga 24: la classe [Teacher] viene esportata in modo che altri script possano importarla;
Lo script [class-04] è il seguente:
// imports
import Enseignant from './Enseignant';
import Personne from './Personne';
// fonction acceptant une personne comme paramètre
function show(personne) {
// dans tous les cas
console.log("paramètre=", personne.toString(), typeof (personne));
// instance de Personne
if (personne instanceof Personne) {
console.log("personne=", personne.toString());
}
// instance de Enseignant
if (personne instanceof Enseignant) {
console.log("enseignant=", personne.toString());
}
}
// appel de show avec un enseignant
show(new Enseignant("Poirot", "Hercule", 66, "détective"));
show(new Personne("Marple", "Miss", 70));
- riga 6: la funzione [show] richiede un tipo [Person] o un tipo derivato;
- riga 8: vengono visualizzati la stringa del parametro e il suo tipo. Troveremo [object];
- righe 10–16: possiamo determinare se si tratta di un tipo [Person] o di un tipo [Teacher]. Il codice può quindi essere adattato al tipo effettivo del parametro;
I risultati dell'esecuzione sono i seguenti:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-04.js"
paramètre= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
paramètre= {"_nom":"Marple","_prénom":"Miss","_âge":70} object
personne= {"_nom":"Marple","_prénom":"Miss","_âge":70}
- righe 4 e 6: JavaScript riconosce correttamente il tipo delle istanze di classe;