Getting started with OOP in Javascript

Why Object Oriented Programming?

Object Oriented Programming (OOP) is a programming paradigm where objects are used to model (represent) real-world or abstract features.

It is the most popular programming paradigm used among software engineers for many reasons, including its:

  • Modularity (ability to be broken down into smaller pieces of reusable code)

  • Ability to directly associate real-world problems in terms of code and effective problem solving

  • Flexibility

  • Security and effectiveness

  • Structure and organization, which is well-suited for large, complex code that needs to be actively maintained

In OOP, objects serve as the fundamental building blocks of our applications. They may have some properties (attributes) and methods (functions). By utilizing these properties and methods, objects are capable of producing desired behavior or results.

This article will take a look at OOP in Javascript, the dynamics behind its implementation, and the various methods we can use to implement it. Let's get started!!

Inheritance

Languages that support Object Oriented Programming use inheritance for code reuse and extensibility in the form of either classes or prototypes.

Inheritance is a mechanism where we use an object or a class as a base for another object(prototype-based inheritance) or class(class-based-inheritance), where the new object or class inherits the properties from its parent class or object.

Class-based inheritance

Classes serve as templates from which we can create new objects from. A class may contain attributes and methods, which objects can then inherit if they are linked to that class. In class-based inheritance, objects are created from classes. The objects created from classes are called instances of the class, and this process of creating an instance is called instantiation. Class-based OOP is the most popular and well-developed model of OOP.

A common misconception among developers is that Javascript supports class-based inheritance. Javascript has no support for class-based inheritance, but rather supports prototype-based inheritance. However, with the popularity of class-based inheritance, ES6 classes was introduced to Javascript.

ES6 classes allow developers to mimic the syntax of class-based inheritance, but behind-the-scenes Javascript still performs prototype-based inheritance.

Prototype-based inheritance

Prototype-based programming is another model of OOP in which inheritance is performed by objects being linked to already existing objects(called prototypes), thus making all methods and properties of these prototypes accessible and usable to the newly linked object.

The object linked to the prototype doesn't exactly contain the functions in the prototype. If we call on a method on this object, and the object doesn't contain that method, it can then look for this method in its prototype object to which it is linked, and if it finds this method, it can then execute it on itself.

These prototypes contain already existing methods, which we can then use on objects that are linked to them, and the process where the newly linked object uses methods from the prototype is called delegation.

Let's declare a simple object literal

let person = { name: "Kene", }; console.log(person);

If you take a look at the console, you’ll see this:

In Javascript, every declared object will automatically inherit the Object prototype. You can view the contents of the Object prototype by clicking on the prototype property.

All these methods are contained in the prototype object linked to the person object we created, and because the person object is linked to this prototype object, we can perform all these methods on the Person object.

console.log(person.valueOf()); //returns "{name: 'Kene'}"

While not defined within the Person object itself, this function can still be called on it through prototype delegation.

NOTE: The prototype is also an object, and as such contains a prototype, and this prototype may also contain another prototype, thus creating what is called a prototype chain. This prototype chain ends when we finally get a prototype with the value of null. Simply put, a prototype chain is a series of links between objects, linked through prototypes.

How OOP works in Javascript

Javascript is a prototype-based language. Before ES6 introduced a way of mimicking class-based inheritance in Javascript, we used the traditional method of constructor functions and the Object.create( ) method. ES6 then introduced the ES6 classes into Javascript in 2015. We will discuss these three methods in this article.

Constructor functions

Constructor functions are functions used to create and initialize objects. A constructor function is written just like a normal function, but the only difference between these is that we call a constructor function using the new keyword, and this creates an object with the this keyword now set to the object.

To create a constructor function, we would pass in the properties which we want the object we create to have as arguments into the constructor function. We would then take these arguments we've received and set them as properties in our function using the this keyword, and set the appropriate property to the appropriate argument.

To create a constructor function for a person, we create a function and assign it to a Person variable

const Person = function (firstName, birthYear, occupation) { this.age; //we may also declare a property without assigning it values initially this.firstName = firstName; this.birthYear = birthYear; this.occupation = occupation; };

NOTE: It is a common convention to capitalize the first letter of the constructor function variable.

We then call the new keyword to invoke the constructor function and create an object. We also assign this object to a variable.

const kene = new Person("Kene", 2002, "student"); console.log(kene);

When the new keyword is called, a sequence of steps happens:

  1. A new empty object is created

  2. The constructor function is called, and the this keyword will be set to this newly created object.

  3. This newly created object is then linked to the constructor functions’ prototype property.

  4. The object we created is automatically returned from the constructor function.

If you take a look at the object in your console, you will see this:

The prototype of the new object is the constructor function, and the prototype of the constructor function is the already existing object prototype in Javascript, and we could go further into this prototype chain, until the prototype is null.

Declaring methods for our constructor functions

To declare methods for constructor functions, we do not directly write these methods into the constructor function, as any method declared in such a manner will get re-declared for every new object instance we create, and this would affect the memory usage of the application negatively if we wish to create so many instances.

Instead, we use prototypes and prototypal inheritance to state methods for constructor functions. Objects can then access these methods from the constructor function. So in a case where we have tens of thousands of instances of objects that need to call on any method, they call on it from the constructor function attached as the prototype to this object.

So, to declare methods for our Person constructor function, we would use the .prototype function to attach these methods to our constructor function

Person.prototype.calcAge = function () { this.age = 2022 - this.birthYear; console.log(this.age); }; Person.prototype.greeting = function () { console.log( Hi, I'm ${this.firstName}, and I am a ${this.occupation}, and I am ${this.age} years old. ); };

These methods are now defined as methods in our constructor functions, and you can see in your console, that it is a prototype of the Object

These methods can now be called on any object we create from this constructor function.

kene.calcAge(); //returns 20 kene.greeting(); //returns 'Hi, I'm Kene, and I am a student, and I am 20 years old.'

When we call the calcAge function, the this.age property is then initialized, and we can now also see it in our console when we take a look at the object created.

Object.create()

This is the simplest way to link an object to a prototype. We can use Object.create to manually set the prototype of a newly created object to another object which we must have declared.

We create the object we want to set as the prototype by creating a simple object literal, where we assign all the methods we want the prototype to carry, and then assigning it to a variable, so we can call on it.

Let's begin by creating an object to be assigned as the prototype for the Person class using object.create()

const PersonProto = { calcAge() { console.log(2022 - this.birthYear); }, greeting() { console.log( Hi, I'm ${this.firstName}, and I am a ${this.occupation}, and I am ${ 2022 - this.birthYear} years old. ); }, };

we can now assign this prototype to an object using the Object.create method

const kene = Object.create(PersonProto);

The kene object is an empty object, and its prototype is the PersonProto object we have assigned to it using the Object.proto( ) method. You can take a look at this on the console.

We now understand how we can assign prototypes using this method, but how do we set properties on the empty object we've just created? We do this by creating a function that does this for us in the PersonProto object, and calling on this function immediately after we declare the empty object, and also pass in the required arguments, if any. Basically, this function will work like a constructor function.

The function can be named anything, but let's name ours init.

const PersonProto = { calcAge() { console.log(2022 - this.birthYear); }, greeting() { console.log( Hi, I'm ${this.firstName}, and I am a ${this.occupation}, and I am ${ 2022 - this.birthYear } years old. ); },

init(firstName, birthYear, occupation) { this.firstName = firstName; this.birthYear = birthYear; this.occupation = occupation; }, };

We then call this init function immediately after we declare the new object, passing in the required arguments

const kene = Object.create(PersonProto); kene.init("Kene", 2002, "student"); kene.calcAge(); //returns 20

If you take a look at your console, you'll see that the object is no longer empty, and its properties have been declared by what we specified in the init function

The Object.create( ) method is the least used way of implementing prototypal inheritance in Javascript.

Finally…ES6 Classes

Creating objects with ES6 classes allows us to use a nicer and more modern syntax than the traditional methods of implementing OOP in Javascript. ES6 classes basically make it look like Javascript is a class-based language, but behind the scenes, Javascript is still implementing prototypal inheritance.

Let's now try to implement a Person using a class

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

A constructor method is needed in a class declaration. The constructor is a special function whose purpose is to create and initialize an object instance of a class.

Methods of the class are declared in the class block, with the syntax of regular functions, and these methods are automatically added to the prototype property of the class without us having to use the .prototype property to manually add them.

class Person { constructor(firstName, birthYear, occupation) { this.firstName = firstName; this.birthYear = birthYear; this.occupation = occupation; } //Below are methods of our class calcAge() { this.age = 2022 - this.birthYear; console.log(this.age); } greeting() { console.log( Hi, I'm ${this.firstName}, and I am a ${this.occupation}, and I am ${this.age} years old. ); } }

Objects are also created from classes using the new keyword. We can now call the methods on newly created objects, and they'll work.

const kene = new Person("Kene", 2002, "student"); const precious = new Person("Precious", 2000, "software developer");

kene.calcAge(); //returns 20 precious.calcAge(); //returns 22 kene.greeting(); //returns 'Hi, I'm Kene, and I am a student, and I am 20 years old.' precious.greeting(); //returns 'Hi, I'm Precious, and I am a software developer, and I am 22 years old.'

If you take a look at the object in the console, you'll see that it's still performing prototype-based inheritance behind the scenes from this class object. This is why ES6 classes are called syntactic sugar over the real prototypal inheritance.

Conclusion

This article has taken a look at Object Oriented Programming which is one of the most adopted programming paradigms in the software engineering world. We cleared up the misconception surrounding class-based inheritance and prototype-based inheritance in OOP as well as exposing you to all the ways we can use to implement OOP in our code. Have fun implementing OOP in your next Javascript application.