Skip to content

13. Classes

Image

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 properties are assigned from the outside
f.prop1 = "val1";
f.show = function () {
  console.log(this.prop1);
}
// using f
f.show();

// a function g acting 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 Person {

  // constructor
  constructor(lastName, firstName, age) {
    this.lastName = lastName;
    this.firstName = firstName;
    this.age = age;
  }

  // getters and setters
  get name() {
    return this._name;
  }
  set name(value) {
    this._name = value;
  }

  get firstName() {
    return this._first_name;
  }
  set firstName(value) {
    this._first_name = value;
  }

  get age() {
    return this._age;
  }
  set age(value) {
    this._age = value;
  }

  // toString as JSON
  toString() {
    return JSON.stringify(this);
  }
}

// Call the class
function main() {
  const person = new Person("Poirot", "Hercule", 66);
  console.log("person=", person.toString(), typeof(person), person instanceof(Person));
}

// call to main
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"
person = {"_lastName":"Poirot","_firstName":"Hercule","_age":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]:


// class
class Person {

  // constructor
  constructor(lastName, firstName, age) {
    this.lastName = lastName;
    this.firstName = firstName;
    this.age = age;
  }

  // getters and setters
  get lastName() {
    return this._lastName;
  }
  set name(value) {
    this._name = value;
  }

  get firstName() {
    return this._first_name;
  }
  set firstName(value) {
    this._first_name = value;
  }

  get age() {
    return this._age;
  }
  set age(value) {
    this._age = value;
  }

  // toString as JSON
  toString() {
    return JSON.stringify(this);
  }
}
// export class
export default Person;
  • 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 Person from './Person';

// class
class Teacher extends Person {

  // constructor
  constructor(lastName, firstName, age, subject) {
    super(lastName, firstName, age);
    this.subject = subject;
  }

  // getters and setters
  get discipline() {
    return this._discipline;
  }
  set discipline(value) {
    this._discipline = value;
  }

}

// call the class
function main() {
  const teacher = new Teacher("Poirot", "Hercule", 66, "detective");
  console.log("teacher=", teacher.toString(), typeof (teacher), teacher instanceof Teacher);
}

// call to 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"
teacher = {"_last_name":"Poirot","_first_name":"Hercule","_age":66,"_profession":"detective"} 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 Person from './Person';

// class
class Teacher extends Person {

  // constructor
  constructor(lastName, firstName, age, subject) {
    super(lastName, firstName, age);
    this.subject = subject;
  }

  // getters and setters
  get discipline() {
    return this._discipline;
  }
  set discipline(value) {
    this._discipline = value;
  }

  // redefinition of toString
  toString() {
    return "[Teacher]" + JSON.stringify(this);
  }
}

// Call the class
function main() {
  const teacher = new Teacher("Poirot", "Hercule", 66, "detective");
  console.log("teacher=", teacher.toString(), typeof (teacher), teacher instanceof Teacher);
}

// call to 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"
teacher = [Teacher]{"_last_name":"Poirot","_first_name":"Hercule","_age":66,"_profession":"detective"} 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 Person from './Person';

// class
class Teacher extends Person {

  // constructor
  constructor(last_name, first_name, age, subject) {
    super(lastName, firstName, age);
    this.subject = subject;
  }

  // getters and setters
  get discipline() {
    return this._discipline;
  }
  set discipline(value) {
    this._discipline = value;
  }

}

// export class
export default Teacher;
  • line 24: the [Teacher] class is exported so that other scripts can import it;

The [class-04] script is as follows:


// imports
import Teacher from './Teacher';
import Person from './Person';

// function accepting a person as a parameter
function show(person) {
  // in all cases
  console.log("parameter=", person.toString(), typeof(person));
  // instance of Person
  if (person instanceof Person) {
    console.log("person=", person.toString());
  }
  // instance of Teacher
  if (person is an instance of Teacher) {
    console.log("teacher=", person.toString());
  }
}

// Call show with a Teacher
show(new Teacher("Poirot", "Hercule", 66, "detective"));
show(new Person("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"
parameter= {"_last_name":"Poirot","_first_name":"Hercule","_age":66,"_profession":"detective"} object
person = {"_last_name": "Poirot", "_first_name": "Hercule", "_age": 66, "_profession": "detective"}
teacher = {"_last_name":"Poirot","_first_name":"Hercule","_age":66,"_profession":"detective"}
parameter = {"_last_name": "Marple", "_first_name": "Miss", "_age": 70} object
person= {"_last_name":"Marple","_first_name":"Miss","_age":70}
  • lines 4 and 6: JavaScript correctly recognizes the type of class instances;