Object-Oriented JavaScript

Object literals, discussed in the previous section, provide a means for JavaScript to group together associated variables and functions into one package, making them easier to work with. The drawback with these is that it isn't possible to represent more than one object, with the same variables and functions, without defining multiple, identical, object literals. This is where the Object-Oriented JavaScript Class syntax comes in.

Like object literals, Object-Oriented JavaScript is based around the concept of Objects, where an Object is made up of Attributes, or Variables, that can contain data, and Methods, that describe actions of an Object. Attributes and Methods are collectively known as Members of a Class. A person can be used as a real-world example of an Object. A person has Attributes such as height, weight, eye colour, hair colour and so on, as well as Methods or actions such as, walk, talk and eat.

A Class is a blue print, or template, of an Object that describes its Attributes and Methods. From this template one or more Objects can be created, which is known as instantiating an Object. A Class can be compared to an architects drawing of a building, which describes how it needs to be built. From this drawing, one or more buildings can be made to the specification of the drawing.

In JavaScript, the basic structure of a Class is as follows.

class ClassName {

    constructor() {
        
    }

}

Notice the capitalisation of the first letter of each word in the Class name. This is known as Pascal Case and is the standard method for naming Classes in JavaScript. A 'constructor' is a special type of Method that is executed when a Class is instantiated and is used to initialise the Object, by, for example, setting the values of Attributes. When a Class is instantiated, values can be passed in and used by the constructor to initialise Attributes. 

Below is an example of a Class called 'Greeting', along with how it can be instantiated. This is the minimum that needs to be done to create and instantiate a Class, there are no values passed in and the constructor does not carry out any initialisation.

class Greeting {

    constructor() {

    }

}

// Instance of the class Greeting called 'greeting'.
const greeting = new Greeting();

Adding and Setting Attributes

In order to pass values into a Class when it is instantiated, they must be included in the parentheses, that follow the name of the Class. Corresponding parameters need to exist within the parentheses of the constructor. These values can then be used to set Attributes within the Object, or, instance of the Class.

class Greeting {

    constructor(message, name) {

        // Set values of attributes.
        this.message = message;
        this.name = name;

    }

}

// Instance of the class Greeting called 'greeting'.
const greeting = new Greeting('Hello', 'Fred');

Here, an instance of the 'Greeting' Class is created, similar to the above, except that the values of 'Hello' and 'Fred' are passed in. These values are given the parameter names of 'message' and 'name' in the constructor and are used to set the values of the 'message' and 'name' Attributes. Notice that the 'this' keyword is used to refer to the current Object instance, instead of using the Class name, when referencing the Attributes.

Attributes, such as 'message' and 'name', in the above example can be accessed using either dot notation or bracket notation, as shown below, where the value of the 'message' Attribute is displayed.

document.write(greeting.message);
document.write(greeting['message']);

Similarly, if the value of an Attribute needs to be changed, it can also be done using dot and bracket notation.

greeting.message = 'Goodbye';
greeting['message'] = 'Goodbye'

The two Attributes, 'message' and 'name', can be accessed and used to form a greeting.

document.write(greeting.message + ' ' + greeting.name + '!');

Adding Methods

As well as the constructor Method, it is also possible to add other Methods when required. These Methods carry out the actions of an Object. In the previous example the greeting is formed by concatenating two Attributes together, separated by a space, with an exclamation mark on the end. This formatting of the greeting could be carried out in a Method. The advantage of doing this is that it could be reused any number of times and if the format of the greeting needs changing it only needs to be altered in one place.

The below example adds a method called 'displayGreeting', which uses the 'return' keyword to return a greeting depending on the time of day. Methods are called in the same way that Attributes are referenced, except a set of opening and closing parentheses are added to the end.

class Greeting {

    constructor(message, name) {

        // Set value of attribute.
        this.message = message;
        this.name = name;

    }

    displayGreeting() {

        const today = new Date();
        const hour = today.getHours();

        // Return a greeting depending on the time of day.
        if (hour > 0 && hour < 12) {

            return 'Good morning ' + this.name + '!';

        } else if (hour >= 12 && hour <= 16) {

            return 'Good afternoon ' + this.name + '!';

        } else {

            return this.message + ' ' + this.name + '!';
        }

    }

}

// Instance of the class Greeting called 'greeting'.
const greeting = new Greeting('Hello', 'Fred');

// Call the displayGreeting method and display the result.
document.write(greeting.displayGreeting());

Getters and Setters

It is good practice to provide Getter and Setter Methods for Class Attributes. This allows for things such as validation before an Attribute is set or, checking if a user is authorised to access an Attribute, for example. Even if there is no initial need for any kind of processing before getting or setting an Attribute, for future proofing purposes it is still a good idea to create Getters and Setters for each Attribute where needed. With this in mind, the above example can be re-written as follows.

Both Getter and Setter Methods have been added for the ‘message’ and ‘name’ Attributes. In other programming and scripting languages, such as PHP and C#, it is possible to stop direct access to an Attribute and only have them accessible through Getter and Setter Methods, using what are called Access Modifiers, however, this isn’t possible to do in JavaScript. Even with a Getter and Setter Method for an Attribute, it is still possible to access it directly. The Getter Methods have been utilised when forming the greeting, instead of accessing the Attributes directly.

class Greeting {

    constructor(message, name) {

        // Set values of attributes.
        this.message = message;
        this.name = name;

    }

    getMessage() {

        return this.message;

    }

    setMessage(message) {

        this.message = message;

    }

    getName() {

        return this.name;

    }

    setName(name) {

        this.name = name;
    }

    displayGreeting() {

        const today = new Date();
        const hour = today.getHours();

        // Return a greeting depending on the time of day.
        if (hour > 0 && hour < 12) {

            return 'Good morning ' + this.getName() + '!';

        } else if (hour >= 12 && hour <= 16) {

            return 'Good afternoon ' + this.getName() + '!';

        } else {

            return this.getMessage() + ' ' + this.getName() + '!';
        }

    }

}

// Instance of the class Greeting called 'greeting'.
const greeting = new Greeting('Hello', 'Fred');

// Call the displayGreeting method and display the result.
document.write(greeting.displayGreeting());