facebook youtube pinterest twitter reddit whatsapp instagram

Object Oriented Programming (Classes) in JavaScript (The ES6 Way)

In the previous guide, we went over the ES5 way of creating objects in JavaScript, In this guide, we would go over using classes to instantiate an object, which is super simple compared to the previous(prototype-based) approach.

Before I get started, I'll love to point out that using classes in JavaScript is similar to the prototype-based approach we covered in the last guide, the new ES6 classes just provide some sort of syntactic sugar (a convenient way to write) for the old prototypal approach, and even more future that is not available in ES5 prototype-pattern, you might want to read the previous guide to know how things work under the hood. With that out of the way, let's get started...

Classes and Objects

To fully grasp object-oriented programming, you’ll want to understand the relationship between the class and the object…

Class (Short for Classification) – The class describes the concept of creating an object, there are not necessarily objects themselves, most people consider a class to be a blueprint (as a guide for how the object would be) for creating new objects.

This is super simple if we take an example, suppose we have a class User. The User is an object and is an instance of a class User. You can have multiple Users from just that class, which is why I think of a class as a code template used to create or generate one or more objects.

So, below is an example of creating a class:

class User {
    // Body of the class
}

The User class above is an example of a legal class, we haven’t actually done anything to the class, but we have defined the template of how our object would be structured.

Before we get into setting the class properties, let’s understand how properties work in a class:

Classes can define special variables or functions called properties, the property which is also known as the member variables holds data that can vary from object to object. So for example, users can have different names, different age, and different properties that set them apart, you get the idea.

So, let’s see an example of declaring some properties within our class:

class User {
    firstName = "First Name";
    lastName = "Last Name";
    age = 23;
}

I set up 3 properties and assigned a default value to each of the properties. Here is where it gets interesting, any objects I instantiate from the User class will be prepopulated with default data.

You can access property variables on an object-by-object basis using either the dot operator(.) or the square bracket notation ([]) in conjunction with an object variable and property name, like so:

let Paul = new User();
Paul.age                       // => 23
// or
Paul['age']                    // => 23

I am using the User class as a template for creating the actual object – Paul, and I am doing that using the new operator. As you can see we are using the new operator in conjunction with the name of a class.

The new operator is invoked with a class name as its only operand and returns an instance of that class; in our case, it generates a User object.

In short, I can generate multiple objects from a class, e.g:

let Paul = new User();
let James = new User();

console.log(Paul.age)    // => 23
console.log(James.age)   // => 23

The 2 objects; Paul and James are different objects of the same type generated from a single class.

If all this are still confusing, here is an illustration that would clear thing ups, imagine you have the following channel rack:

1. Channel Racks

We would assume this Channel rack is the class, it has 4 properties, the kick, clap, hat, snare, and the default pattern of how they are all gonna be played.

Here is the thing, the class defines the template of how the object is gonna be modeled, so, in the above example, I am setting properties of how I want my object to look, to make the class do actual work, I’ll need to create an instance of the class. if I like I can manipulate the patterns of the kick in a different object entirely or the snare in another object:

Class_objects Ilustration

As you can see from a single set of classes, we created different instances of the class known as objects with different manipulations of the properties, so, the class is merely a template of how our real objects would be structured, they are not the actual object.

If you can understand the illustration, then you’ve grasped the basic idea of the relationship between class and objects.

Now, let’s go back to our code, here is the class we are working on:

class User {
    firstName = "First Name";
    lastName = "Last Name";
    age = 23;
}

and here is the object we created:

let Paul = new User();
let James = new User();

console.log(Paul.age)    // => 23
console.log(James.age)   // => 23

Don't worry about the two objects printing the age: 23, you can still change the value of the property of a certain object without affecting the other object, in short, we can do that just now:

let Paul = new User();
let James = new User();

Paul.age = 50             // => Change Pauls age
James.age = 60            // => Change James age

console.log(Paul.age)    // => 50
console.log(James.age)   // => 60

Cool right?

Really, you don’t have to declare all the properties in the class, you can as well add it dynamically like so:

James.interest = "Football"

console.log(James.interest);   // => Football

Now, if we take a sneak peek at the object, you would in fact see that the properties have been dynamically added:

console.log(James)

// => User {firstName: "First Name", lastName: "Last Name", age: 60, interest: "Football"}


As you can see, I do not even need to declare the properties in the class itself, I am doing it dynamically, but this approach is generally frowned upon, declare properties dynamically can create unnecessary confusion, the worst thing about setting a property dynamically is that JavaScript won’t warn you if you misspell a property name, why would it anyway?

So, for example, if the actual property is firstName and I do:

Paul.firstNae = "James";

JavaScript interpreter would consider that as legal as you are declaring the property dynamically, and not changing the property value of the firstName, where you would get a problem is when referring to the correct property name when working with it, in short, just bury the idea of setting a property dynamically, we would see a way we can fix incorrect spellings later on with a constructor method.

Now, all we've been doing is creating class, properties, and working with the object's properties from outside the class, this is what we've been doing:

class User {
    firstName = "First Name";
    lastName = "Last Name";
    age = 23;
}

// Instantiate new object
let Paul = new User();
let James = new User();

Paul.age = 50                 // Change Pauls age
James.age = 60              // Change James age

console.log(Paul.age)    // => 50
console.log(James.age)   // => 60

Imagine doing this for 7 instances of that class, that's cumbersome, so, let me introduce you to...

Methods

We've seen an example of one property, which is a variable, a function (known as a method within a class) is also a property. I know when I say object properties can also be functions, it might be confusing, but here is the thing, functions are nothing more than properties that hold function values, they are still properties of something.

The only difference between a standard function and a function declared within a class is that a method or a function within a class only works with that class.

So, let's say you want to create a method that returns users' first name, last name, and age, you can do:

// Create a class User
class User {
    firstName = "First Name";
    lastName = "Last Name";
    age = 23;

    // A method to format user's name and age
    formatuser() {
        return `Name: ${this.firstName} ${this.lastName}\nAge: ${this.age}`;
    }
}

// Instantiate a new object "James" from the class User
let James = new User();

// Set object properties value
James.firstName = "James";
James.lastName  = "Cotlin";
James.age       = 50;

console.log(James);

// Output =>
/*
Name: James Cotlin
Age:   50
*/

The function we created is nothing new, If you have no idea how functions work in general, please read this guide: Guide To Functions In JavaScript then come back to this guide.

I am adding the formatuser() method to the User class, The this pseudo-variable in the method is used to refer to an object instance properties from a class. This is like calling whatever stuff you have in your object into the class, if it can't find anything, it would use the default properties you set in the class.

The formatuser() method combines and returns the firstName, lastName, and age properties, which saves us time from formating every instance of an object that wants to use that same formatting.

Here is another example of multiple objects using the same format:

// Instantiate a new object "James" from the class User
let James = new User();

// Set object properties value
James.firstName = "James";
James.lastName  = "Cotlin";
James.age       = 50;

console.log(James.formatuser());

// Output =>
/*
Name: James Cotlin
Age:   50
*/

// Instantiate a new object "Bobby" from the class User
let Bobby  = new User();

Bobby.firstName = "Bobby";
Bobby.lastName  = "Gerrad";
Bobby.age       = 77;

console.log(Bobby.formatuser());

// Output =>
/*
Name: Bobby Gerrad
Age: 77
*/

As you can see, we can reuse the same formating over and over again with a new objects, and all this is possible by creating a method within the class.

There is still a little issue, whenever we need a new object with a different property value, we need to write it out outside of the class, it would be crazy if you are trying to instantiate 50 objects with different properties. What if we can somehow create a method that is called automatically when an object is instantiated from a class, recall I said, I'll show you avoid misspelled property names, and we can do that with...

Constructor Method

A constructor method is invoked when an object is created. You can use it to prepare the essential properties, and save the hassle of calling the property every time you need to set a new value, now, I can remove the properties we define in the root of the class, and instead put them in the constructor like so:

// Create a class User
class User {
    constructor(firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    // A method to format user's name and age
    formatuser() {
        return `Name: ${this.firstName} ${this.lastName}\nAge: ${this.age}`;
    }
}

To then instantiate a new object, you simply pass the argument to the object like so:

// Instantiate a new object "James" from the class User
let James = new User('James', 'Gerrard', 77);

console.log(James.formatuser());

// Output =>
/*
Name: James Gerrard
Age:   77
*/

Isn't that neat, instead of having to call the property name every time we need to set a new value, we simply prepare the necessary ingredient in the constructor, and whenever we instantiate a new object, the constructor takes care of the property, so, we only need to pass the value.

Static Method and Properties

I previously said class describes the concept of creating an Object, there are not necessarily the objects themselves, but as a code template used to create or generate one or more objects.

Well, it isn’t quite black and white, you can in fact access both methods and properties in the context of a class rather than that of an object when using static methods and properties, a value of a static method is not relative to a certain object instance but to the class itself.

Meaning, the static methods are functions with class scope, you don’t have to instantiate an object to access them, although they cannot access any normal properties in the class because, of course, it would belong to an object, however, they can access static properties.

Normally, to access an element via an object, we would do something like:

artisteInfo.getArtisteInfo()

This kinda makes sense, since we are accessing via an object, it is different when accessing a static element via a class, you do not need a variable that references an object. Instead, you use the class name in conjunction with whatever you are method or property you are referring to:

StaticExample.passValidate();

Before we go further, what is the purpose of static methods and properties?

You can use a static method if you don’t want the functionality of a method to depend on an object instance, in other words, if something is not gonna change often, use a static method.

An example of what is not gonna change often is a minimum or maximum password length, a image format also isn't gonna change often, you get the idea, so, here is an example that checks minimum password length:

class Form {

    static passLen(pass){   // Static Method
        const minPassLen = 8;           // Set the minimum password length
        const passLen = pass.length;    // find the length of the pass passed in by the user and store in the passLen variable
        return passLen >= minPassLen;   // If the passLen that we get above is greater than 8 (minPassLen), return the passLen
    }
}

let password = 'Iambigg';
console.log((Form.passLen(password)) ?  'Password valid' :  'Password Too Short');

// Output =>
// Password Too Short

We are using the (.)length property to test the length of the pass that is passed in and should be greater than or equals to the property value (minPassLen), which is 8. So, if it is greater or equals to 8, it would return true otherwise false.

This:

 return passLen >= minPassLen;

can also be written as:

if(passLen >= minPassLen) { return true; }
else { return false; }

or using a ternary operator:

return passLen >= minPassLen ? true : false;

As you can see, the first one is shorter and doesn't require many lines of code...moving on...

Outside of the class, we then pass a value:

let password = 'Iambigg';
console.log((Form.passLen(password)) ?  'Password valid' :  'Password Too Short');

If the password we passed in is lesser than 8, it would return "Password Too Short", if otherwise, it would return password valid. All this is programmatically possible by just passing an argument to the static method.

As you can see, we don't have to instantiate any new objects, it just works right off the bat.

Note, this:

console.log((Form.passLen(password)) ?  'Password valid' :  'Password Too Short');

can be written as:

let password = 'Iambigg';
if(Form.passLen(password)){ console.log('Password valid') }
else { console.log('Password Too Short'); }

I don't know about you, but I'll for sure go with the shorter syntax also known as the ternary operator.

Inheritance

Inheritance in JavaScript classes is the means by which one or more children's classes can be derived from a parent class. When a class inherits from another class, it is said to be a subclass of that class.

A child's class is derived from and inherits characteristics from the parent. The characteristic can be properties, methods, and even both.  The child class will typically add new functionality to that provided by its parent (also known as a superclass); for this reason, a child class is said to extend its parent.

To better understand this, let's assume I am building a class around musical records that would contain musical artists, their songs, and the genre of music they make.

I can do something like the following:

class MusicalRecords {

    constructor(artisteName,
                songTitle,
                genreName,
                genreDescription) {

        this.artisteName = artisteName;
        this.songTitle = songTitle;
        this.genreName = genreName;
        this.genreDescription = genreDescription;

    }

        // methods to get the artiste details
         artisteSummary() {
            return `Name: ${this.artisteName} \nSong: ${this.songTitle}`
        }
}

let Fela = new MusicalRecords('Fela', 'Colonial Mentality')
console.log(Fela.artisteSummary());

// Output =>
Name: Fela
Song: Colonial Mentality

We have a couple of issues, and the first one is the fact that the object is inheriting unnecessary and unused properties, if we take a sneak peak at the object, we would get:

console.log(Fela)
// => MusicalRecords {artisteName: "Fela", songTitle: "Colonial Mentality", genreName: undefined, genreDescription: undefined}

The unused property got filled up with undefined, if you are not using them, then why create them, if you are new to creating classes, you would come across a scenario where you want a method to do a certain thing, for example, you can create one that format the artisteName and songTitle, and another that formats the genreName, and the genreDescription.

Here is what you can do, when building this kind of stuff, keep an eye on the elements of the base class that don’t fit together or that need to be handled differently, we move the ones that we feel might be unique to each object out of the base class, and leave the elements that can be generalized in the base class, in our case, we can move out the genre properties into its own class:

class MusicalRecords {

    constructor(artisteName, songTitle) {

        this.artisteName = artisteName;
        this.songTitle = songTitle;

    }

        // methods to get the artiste details
         artisteSummary() {
            return `Name: ${this.artisteName} \nSong: ${this.songTitle}`
        }

}

// new class to handle genre
class MusicalGenres {

    constructor(genreName, genreDescription) {

        this.genreName = genreName;
        this.genreDescription = genreDescription;

    }

    // methods to get the genre details
    genreSummary() {
        return `Genre: ${this.genreName} \n Genre Description: ${this.genreDescription}`
    }

}

Easy enough, if we want to create artiste records, we instantiate from the MusicalRecords, and if we need a genre, we instantiate from the MusicalGenres without passing redundant data.

There is a but!

What if we create another class that returns the number of streams of an artiste song, but you also want the class to inherited the method and properties in the MusicalRecords class, how do we go about that?

Inheritance to the rescue:

class MusicalRecords {

    constructor(artisteName, songTitle) {

        this.artisteName = artisteName;
        this.songTitle = songTitle;

    }
        // methods to get the artiste details
         artisteSummary() {
            return `Name: ${this.artisteName} \nSong: ${this.songTitle}`
        }

}

// A class that extends or inherit properties and method from the parent class
class ArtistePlays extends MusicalRecords { // Inherit MusicalRecords
    constructor(artisteName, songTitle, noOfPlays) {
        super(artisteName, songTitle); // super methods calls the parent class constructor

        this.noOfplays = noOfPlays; // The new properties.
    }

}

let Fela = new ArtistePlays('Fela', 'Colonial Mentality')
console.log(Fela.artisteSummary());

// Output =>
Name: Fela
Song: Colonial Mentality

When inheriting, you must pass the argument from the base class in the order that they are specified in the base or parent class.

So, first thing first we passed in the properties arguments and added an extra one (noOfPlays, this is unique to the ArtistePlays class), we then use the super method to copy over properties from one constructor into another constructor, this would avoid us from redefining the properties all over again, plus it is also inherited, so, whatever behavior you've done in the base class would always be copied above.

So, if for example, a property in the base constructor has to do with a calculation, we can always change the calculation in the base class, without having to change it in other places.

As you can see, the methods are also inherited, which is why I was able to use the artisteSummary() method.

so, now, let's modify the getSummary method in the ArtistePlays class to soothe what we want:

// A class that extends or inherit properties and method from the parent class

class ArtistePlays extends MusicalRecords { // Inherit MusicalRecords

    constructor(artisteName, songTitle, noOfPlays) {
        super(artisteName, songTitle); // super methods calls the parent class constructor

        this.noOfplays = noOfPlays; // The new properties.
    }

    // methods to get the artiste details
    artisteSummary() {
        return `Name: ${this.artisteName} \nSong: ${this.songTitle} \nNo of Plays: ${this.noOfplays}`
    }


}

let Fela = new ArtistePlays('Fela', 'Colonial Mentality', 999000000)
console.log(Fela.artisteSummary());

// Output =>
/*
Name: Fela
Song: Colonial Mentality
No of Plays: 999000000
*/

The method would override the method in the base class.

I hope you are able to wrap your head around this, let me know if you have any question in the comment section below, good luck ;)

Related Post(s)

  • Getting Started With JavaScript (Introduction)

    This is my first JavaScript guide on this blog, and in this guide, we would go over the introduction of JavaScript, but first… What is JavaScript? JavaScript is a client-side scripting language su

  • Operator in Javascript

    Operators in javascript can either be used for logical expression where you connect two or more expressions and can be a comparison expression where you compare two values. You can also use an operat

  • Exploring Data Types, and Variables in JavaScript

    In JavaScript or any programming language, a data type is an attribute of data that tells the interpreter how the programs intend to use the given data. JavaScript support a couple of data type whic

  • Creating a Loader and Remove it After a Specific Time Using JavaScript/CSS/SVG

    In this guide, we would create a loader, and remove it after a specific time using CSS and JavaScript. The icon would be in SVG format, and the rotation would be done in CSS, while the removal after

  • Object Oriented Programming in JavaScript (The ES5 Way)

    In this guide, you'll learn and understand how to create and use Objects in JavaScript (ES5). While ES6 already supports creating classes (which is the way you create objects in Java, PHP, etc), they

  • Working With The Local & Session Storage In JavaScript

    We previously take a deep dive into the Windows Object, Properties, Methods, and even the Document Object itself, in this guide, we would explore working with the local and session storage in JavaScr