The Basics of JavaScript Classes from a Junior Dev Point of View
Published: 2023-02-02 01:23:22

**Introduction:**

Classes are blueprints/templates for creating objects based on predefined properties and methods.

This is useful for when a developer needs to create multiple objects containing similar properties and methods. However, the object created from the Class is not limited only to the predefined properties and methods. The object can have additional properties and methods added after it’s been created. 

The practice of using Classes creates organized and reusable code that clearly defines the properties, methods, and behavior of created objects. 



**An example of a Class:**

Here we will create a class called Developer with a few predefined properties:
```js
class Developer {
    constructor(name, role, favouriteMusic) {
        this._name = name;
        this._role = role;
        this._favouriteMusic = favouriteMusic;
    }
}
```

We've created a Class to now create a Developer object. You will pass 3 of the following values into it when creating an object: name, role, and favourite music.
You might be asking what the constructor method is and why the variables are being saved with an underline eg. 
this._name.

A constructor enables you to provide any custom initialization that must be done before any other methods can be called on an instantiated object. In simple terms, this is where you connect the passed in variables to the desired properties.
The underline is used to indicate that these properties are not meant to be directly called when targeting the objects created from the Class. To call these properties we will be using a method called getter.

Let's add a getter method to the Class! Let's simply return the properties value using the get syntax:
```js
class Developer {
    constructor(name, role, favouriteMusic) {
        this._name = name;
        this._role = role;
        this._favouriteMusic = favouriteMusic;
    }
    get name() {
        return this._name; 
    }
    get role() {
        return this._role; 
    }
    get favouriteMusic() {
        return this._favouriteMusic; 
    }
}
```

Now we have a way of accessing the properties value that does not calculate the property's value until it is accessed. A getter defers the cost of calculating the value until the value is needed.


**So we have a Class created. What now?**


Now we can simply create an object with our new Class:
```js
const lukeDeveloper = new Developer('Luke', 'Front-end', 'Alternative Rock');
```

Sweet! We've now made it really easy to add developers to our team! Let's add one more:
```js
const claytonDeveloper = new Developer('Clayton', 'Back-end', 'EDM');
```

We now have Luke and Clayton with their details saved in two separate objects based off the same Class!
If we'd like to access one of the properties, eg. role, we can use the getter methods we created to simply do that:
```js
claytonDeveloper.role // 'Back-end'
```

However, as stated above, we are not limited to the predefined properties, let's add a custom one to Clayton real quick:
```js
claytonDeveloper.operatingSystem = 'Mac';
```

We've now added a new property called operatingSystem with the value of Mac to the claytonDeveloper object. 


**Let's go a bit deeper. Let's talk about Class inheritance.**


So far we've demonstrated how a Class is a nice way to create objects based off a blueprint/template. Now what if we are also tasked with creating objects of our Designer team? Would it make sense to create an entirely new Class for the Designers? If they share similar properties then it's better to use Class inheritance through extending a Parent Class. 

Let's say for example, the Designers share all the same properties as our Developers, but have one more property eg. what Design Software they use. 

So what we will do, is create a Parent Class that will house all the properties, getters, setters, and methods that are shared amongst the Classes that we'll be extending off of it, called subclasses.

Let's jump right in. Let's create that Parent Class and call it Employee:
```js
class Employee {
    constructor(name, role, favouriteMusic) {
        this._name = name;
        this._role = role;
        this._favouriteMusic = favouriteMusic;
    }
    get name() {
        return this._name; 
    }
    get role() {
        return this._role; 
    }
    get favouriteMusic() {
        return this._favouriteMusic; 
    }
}
```

Since the Developer Class shares all the same properties, let's quickly create that subclass using the extends keyword:
```js
class Developer extends Employee {
    constructor(name, role, favouriteMusic) {
        super(name, role, favouriteMusic);
    }
}
```

You might have noticed something new here! That would be the super keyword. Since every Class needs a constructor, that is the first thing added, the super keyword is for when you're extending a Class to inherit values from the Parent Class. So since the Developer Class we just created using the extends keyword shares all the same values as the Parent, we can pass every value into the super keyword. Using the super keyword by default will carry over all the Parent's methods into the subclass. 

**Let's now do the same for Designers:**
```js
class Designer extends Employee {
    constructor(name, role, favouriteMusic, designSoftware) {
        super(name, role, favouriteMusic);
        this._designSoftware = designSoftware;
    }
    get designSoftware() {
        return this._designSoftware;
    }
}
```

This is the fun part about extending Classes. You can see here that since Designers share the same properties as Developers, we could use the super keyword to bring them all over, however, you can see the fourth variable being passed through designSoftware will require a property to be set and then a getter - to have consistency with the other getter methods. It's very important to first call the super keyword, even if there's no values being passed through eg. super() because if you don't call it on the top of the constructor method when extending a Class you will have an error thrown.  

Let's quickly talk about setters. A setter is used to set a value of an Object's property. Let's create a setter for the designSoftware within the Designers Class:
```js
class Designer extends Employee {
    constructor(name, role, favouriteMusic, designSoftware) {
        super(name, role, favouriteMusic);
        this._designSoftware = designSoftware;
    }
    get designSoftware() {
        return this._designSoftware;
    }
    set designSoftware(software) {
        this._designSoftware = software;
    }
}
```

As you can see, the setter and the getter can share the same name. As you can see the setter takes in an argument and sets the property to the passed through value. Let's put this to work. First, let's create a designer object:
```js
const maggieDesigner = new Designer('Maggie', 'Junior Designer', 'Pop', 'Figma');
```

Sweet! We've now created a designer object from the Designer Class. However, let's say Maggie has found that she prefers Adobe XD over Figma. Let's use that setter to update that properties value!
```js
maggieDesigner.designSoftware = 'Adobe XD';
```

Now if we were to call the getter method for the designSoftware property, we would get this:
```js
console.log(maggieDesigner.designSoftware); // 'Adobe XD'
```

Awesome, we've now successfully used setters and getters. Finally let's talk about Methods.


**Let's wrap it up with Methods!**

Class methods are created with the same syntax as object methods. Let's create a method that simple logs out the details of the employee. We will be adding this to the Employee Parent Class so the subclasses will inherit the Method:
```js
class Employee {
    constructor(name, role, favouriteMusic) {
        this._name = name;
        this._role = role;
        this._favouriteMusic = favouriteMusic;
    }
    get name() {
        return this._name; 
    }
    get role() {
        return this._role; 
    }
    get favouriteMusic() {
        return this._favouriteMusic; 
    }
    logEmployeeDetails() {
        console.log(`Name: ${this._name} | Role: ${this._role} | Favourite Music: ${this._favouriteMusic}`);
    }
}
```

Now when we append the method onto maggieDesigner we get this:
```js
maggieDesigner.logEmployeeDetails() // Name: Maggie | Role: Junior Designer | Favourite Music: Pop
```

However, as you can see, we are missing the new designSoftware property we added to the Designer Class. So what shall we do?

Let's simply update the method within the Designer subclass to include the new property:
```js
class Designer extends Employee {
    constructor(name, role, favouriteMusic, designSoftware) {
        super(name, role, favouriteMusic);
        this._designSoftware = designSoftware;
    }
    get designSoftware() {
        return this._designSoftware;
    }
    set designSoftware(software) {
        this._designSoftware = software;
    }
    logEmployeeDetails() {
        console.log(`Name: ${this._name} | Role: ${this._role} | Favourite Music: ${this._favouriteMusic} | Design Software: ${this._designSoftware}`);
    }
}
```

Now when we call the method onto maggieDesigner we get this output:
```js
maggieDesigner.logEmployeeDetails() // Name: Maggie | Role: Junior Designer | Favourite Music: Pop | Design Software: Adobe XD
```

This wraps up the basics of Classes. There are so much more you can do with Classes and this is only touching the surface. I suggest digging into your favourite documentation and making tests within your IDE to try to get a good grasp of each concept.