13. Classes

Here we introduce ECMAScript 6 classes. First, we show that functions can already be used as classes.
13.1. script [class-00]
The following script demonstrates an unusual use of functions. Here, they are used as objects.
'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();
Comments
- lines 5–7: the body of function f does not define any properties;
- lines 9–12: properties are assigned to the function f from outside;
- line 14: use of the function (object) f. Note that we do not write [f()] but simply [f]. This is the notation for an object;
- lines 17–22: we define a function [g] as if it were a class with properties and methods;
- line 24: the function [g] is instantiated by [new g()];
Execution results
[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 introduced the concept of classes, which now allows us to avoid using functions to create classes.
13.2. script [class-01]
The [class-01] script presents a [Person] class:
// 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();
Comments
- line 2: the keyword [class] denotes a class;
- lines 5–9: the keyword [constructor] denotes the class constructor. There can be at most one. It is used to construct and initialize an instance of the class. Note that there is no declaration of the properties [last_name, first_name, age];
- lines 11–36: class properties. Here we see elements already covered in the section on objects 4, page 44. Only the syntax differs;
- line 41: creation of an object of type [Person]. From now on, the object [person] is used as a literal object. [typeof (person)] evaluates to “object” and the expression [person instanceof (Person)] is true. It is therefore possible to determine the exact type of a class instance;
Execution results
[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]
This script demonstrates the ability to inherit from a class using the [extends] keyword.
First, we move the [Person] class into a module called [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;
- line 39: we export the [Person] class so that scripts can import it;
The [class-02] script creates a [Teacher] class derived from the [Person] class:
// 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();
Comments
- line 2: we import the [Person] class from the [Person.js] module, which is located in the same folder as [class-02];
- line 5: the [Teacher] class extends (inherits from) the [Person] class using the [extends] keyword: it adds a [_subject] property with the corresponding getter and setter methods;
- lines 8–11: the constructor of the [Teacher] class receives four values to initialize the class’s four properties;
- line 9: the keyword [super] calls the constructor of the parent class [Person], which will therefore initialize the properties [_last_name, _first_name, _age];
- line 10: the [_discipline] property belonging to the [Teacher] class is initialized;
- lines 14–19: the getter and setter for the [_discipline] property;
- line 25: an object of type [Teacher] is created;
- line 26: we use the method [teacher.toString()]. The [Teacher] class does not have this method. Therefore, the method from its parent class is automatically used. This method renders the expression [JSON.stringify(this)], where [this] will be an [Teacher] object here, not a [Person] object. This is what is known in object-oriented programming as class polymorphism. A big word for JavaScript, which is not an object-oriented language. Nevertheless, JavaScript does what is expected of it here: it correctly displays a teacher;
The results of the execution are as follows:
[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
- line 2: JavaScript correctly recognizes that the variable [teacher] is of type [Teacher];
13.4. script [class-03]
The [class-03] script shows that a child class can override properties and methods of its parent class. Here, we override the [toString] method of the parent class:
// 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();
The results of the execution are as follows:
[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]
The [class-04] script again demonstrates polymorphism in action: where a function expects a formal parameter of type [Person], we can pass a derived type such as [Teacher]. Indeed, by virtue of derivation, the [Teacher] type has all the attributes of the [Person] type.
First, we isolate the [Teacher] type in a [Teacher.js] module:
// 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;
- line 24: the [Teacher] class is exported so that other scripts can import it;
The [class-04] script is as follows:
// 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));
- line 6: the [show] function expects a [Person] type or a derived type;
- line 8: the parameter’s string and its type are displayed. We will find [object];
- lines 10–16: we can determine whether it is a [Person] type or a [Teacher] type. The code can therefore be adapted to the actual type of the parameter;
The execution results are as follows:
[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}
- lines 4 and 6: JavaScript correctly recognizes the type of class instances;