Categories
JavaScript Basics

Using Classes in JavaScript

Spread the love

Classes in JavaScript are a special syntax for its prototypical inheritance model that is a comparable inheritance in class-based object oriented languages. Classes are just special functions added to ES6 that are meant to mimic the class keyword from these other languages. In JavaScript, we can have class declarations and class expressions, because they are just functions. So like all other functions, there are function declarations and function expressions.

Classes serve as templates to create new objects.

The most important thing to remember: Classes are just normal JavaScript functions and could be completely replicated without using the class syntax. It is special syntactic sugar added in ES6 to make it easier to declare and inherit complex objects.

Defining Classes

To declare a class, we use the class keyword. For example, to declare a simple class, we can write:

class Person{
  constructor(firstName, lastName) {
    this.firstName= firstName;
    this.lastName = lastName;
  }
}

Class declarations aren’t hoisted so they can’t be used before they are defined in the code, as the JavaScript interpreter will not automatically pull them up to the top. So the class above won’t work before it’s defined in the code like the following:

const person = new Person('John', 'Smith');class Person{
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

We will get a ReferenceError if we run the code above.

We can also define a class by a class expression, which is an alternative syntax. They can be named or unnamed. We can also assign a class to a variable like we do with functions. If we do that, we can reference the class by its name. For example, we can define:

let Person = class {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

To get the name of the unnamed classes above, we can get the name with the name property, like so:

console.log(Person.name);

We can also define a named class like the following:

let Person = class Person2 {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

Then to get the name of the class, we can use the name property again. So we if we write:

console.log(Person.name)

We get Person2 logged.

The class body is defined with curly brackets. We define the class members inside the brackets. The body of the class is executed in strict mode, so everything defined in strict mode applies to the definition of a class, so we can’t define variables without keyword before it like var , let or const, and many other rules apply when you define a class.

Classes in JavaScript also have a constructor method that lets us set fields when the object is instantiated with a class. Each class can only have one constructor method in it. If there’s more than one, then SyntaxError will be thrown. A constructor can also call the super method to call the constructor of the super class if the class extends a parent class.

Methods that aren’t declared static constitutes the prototypical methods of the class. They are called after an object has been created by using the new keyword. For example, the following class has only prototypical methods:

class Person{
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  } get fullName(){
    return `${this.firstName} ${this.lastName}`
  } sayHi(){
    return `Hi, ${this.firstName} ${this.lastName}`
  }
}

In the Person class above, fullName and sayHi are prototypical methods. They are called like this:

const person = new Person('Jane', 'Smith');
person.fullName() // 'Jane Smith'

Static methods are methods that can be called without creating an object from the class using the new keyword. For instance, we can have something like the following:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
  sayHi() {
    return `Hi, ${this.firstName} ${this.lastName}`
  }
  static personCount() {
    return 3;
  }
}

We can call the personCount function without using the new keyword to create an instance of the class. So if we write:

Person.personCount

We get 3 returned.

The this value inside prototypical methods will be the value of the object. For static methods the value of this has the class that the static method is in as the value.

<img alt="Image for post" class="s t u dc ai" src="https://miro.medium.com/max/11426/0BvO6QcBmjOGtcHnL" width="5713" height="3809" srcSet="https://miro.medium.com/max/552/0BvO6QcBmjOGtcHnL 276w, https://miro.medium.com/max/1104/0BvO6QcBmjOGtcHnL 552w, https://miro.medium.com/max/1280/0BvO6QcBmjOGtcHnL 640w, https://miro.medium.com/max/1400/0*BvO6QcBmjOGtcHnL 700w" sizes="700px"/>

Photo by Thomas Kelley on Unsplash

Getters and Setters

JavaScript classes can have getter and setter functions. Getters, as the name suggests, is a method that lets us get some data from a class. Setters are methods that gives us the ability to set some fields of the class. We denote getter functions with the get keyword and setters with the set keyword. For example, we can write a class that has getters and setters like the following:

class Person {
  constructor(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;
  }
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
  get firstName() {
    return this._firstName
  }
  get lastName() {
    return this._lastName
  }
  sayHi() {
    return `Hi, ${this.firstName} ${this.lastName}`
  }
  set firstName(firstName) {
    this._firstName = firstName;
  }
  set lastName(lastName) {
    this._lastName = lastName;
  }
}

Then when we use the new keyword to construct a Person object, we can use them in the following way:

const person = new Person('Jane', 'Smith');
person.firstName = 'John';
person.lastName = 'Doe';
console.log(person.firstName, person.lastName)

Since we have the getter and setter functions, we can use them to set the data directly to set the data for firstName and lastName of the Person class. In the setter functions, which start with the keyword set, when we assign to a value them, it gest passed into the parameters and set in the member of the class. In the getter functions, which are denoted by get we return the member values which triggers the associated get function for the value.

JavaScript Inheritance

In JavaScript, we can create classes where the properties can be included in the properties of a child class.

So, we can have a high-level class that contains the properties that are common to all the child classes, and the child class can have its own special properties that are not in any other classes.

For example, if we have an Animal class with the common properties and methods, like name and the eat method, then the Bird class can just inherit the common properties in the Animal class. They don’t have to be defined in the Bird class again.

We can write the following to do inheritance in JavaScript:

class Animal {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log('eat');
  }
}class Bird extends Animal {
  constructor(name, numWings) {
    super(name);
    this.numWings = numWings;
  }
}const bird = new Bird('Joe', 2);
console.log(bird.name)
bird.eat();

In the example above, we have the parent class, Animal, that has the eat method, which all classes that extends from Animal will have, so they don’t have to define eat again.

We have the Bird class which extends the Animal class. Note that in the constructor of the Bird class, we have the super() function call to call the parent’s class constructor to populate the properties of the parent class in addition to the properties of the child class.

Classes cannot extend regular objects, which cannot be constructed with the new keyword. If we want to inherit from a regular object, we have to use the Object.setPrototypeOf function to set a class to inherit from a regular object. For example:

const Animal = {
  eat() {
    console.log(`${this.name} eats`);
  }
};class Cat{
  constructor(name) {
    this.name = name;
  }
}class Chicken{
  constructor(name) {
    this.name = name;
  }
}Object.setPrototypeOf(Cat.prototype, Animal);
Object.setPrototypeOf(Chicken.prototype, Animal);let cat = new Cat('Bob');
let chicken = new Chicken('Joe');
cat.eat();
chicken.eat();

If we run the example code above, we can see Bob eats and Joe eats logged because we have inherited the eat function from the Animal object.

this Keyword

The this keyword allows us to access the current object’s properties inside the object, unless you’re using arrow functions.

As we can see from the above example, we can get the properties of the instance of the child and the parent class in the object.

Mixins

We can use mixins to do multiple inheritance in JavaScript. Mixins are templates for creating classes. We need mixins to do multiple inheritance because JavaScript classes can only inherit from one super class, so multiple inheritance isn’t possible.

For example, if we have a base class, we can define mixins to incorporate the members from multiple classes into one by composing the mixins by calling one and then pass the returned result into the next one as the argument, and so on, like the following:

class Base {
  baseFn() {
    console.log('baseFn called');
  }
}let classAMixin = Base => class extends Base {
  a() {
    console.log('classAMixin called');
  }
};let classBMixin = Base => class extends Base {
  b() {
    console.log('classBMixin called');
  }
};class Bar extends classAMixin(classBMixin(Base)) {}
const bar = new Bar();
bar.baseFn()
bar.a()
bar.b()

In the code above, we have the Base class which we pass into the classBMixin to get the b function into the Base class, then we call the classAMixin by passing in the result of classBMixin(Base) into the argument of the classAMixin to return the a function from classAMixin into the Base class and then return the whole class with all the functions from all the classes incorporated into one.

If we call all the functions above like we did by creating an instance of the Bar object and then call the baseFn, a, and b functions, we get:

baseFn called
classAMixin called
classBMixin called

This means that we have all the functions from the mixins incorporated into the new Bar class.

In JavaScript, classes are just syntactic sugar to make the prototypical inheritance of JavaScript clearer by letting us structure the code in a way that’s more like typical inheritance in class-based object oriented inheritance pattern. This means that we write classes to use the new keyword to create objects from the classes, but underneath the syntactic sugar, we are still using prototypical inheritance to extend objects. We can extend classes from objects and we can also use mixins to do multiple inheritance in JavaScript classes.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *