DEV Community

Cover image for OOP in JavaScript. #1. Constructor and Prototypes
Abdelrahman AbouDeghedy
Abdelrahman AbouDeghedy

Posted on

OOP in JavaScript. #1. Constructor and Prototypes

Introduction

Hi! Today I will start a series, in which I will talk about OOP in Javascript. I will start by explaining constructor functions and prototypes.

OOP in JavaScript

OOP is a programming paradigm (style of code) based on the concept of objects.

OOP in JS is different from the classical OOP.

  • In classical OOP we have objects that are instantiated from classes.
  • In JS OOP, we create objects, then we link them to a prototype object (that got all the methods), then the objects inherit all the methods of the prototype (It can be also said that: the prototype delegates the methods to the objects).

How can we create objects, their Prototype, and Linking Them Together?

We have three ways in JS to achieve that:
1- Constructor Functions.
2- ES6 Classes.
3- Object.create ().

Today we will discuss the first one, which is: Constructor Functions.

Constructor Functions

When we create a constructor function, the convention is to start its name with a capital letter.

Arrow function won't work as a constructor function, because it doesn't have its own this keyword.

This constructor function can be used to make any number of objects as we want.

The Difference Between Calling a Constructor Function and Calling a Normal Function:

When we call a constructor function, we use the new keyword.

Example:

const Person = function (firstName, birthYear) {
    this.firstName = firstName;
    this.birthYear = birthYear;
}

// Making an instance of the constructor function
const Abdelrahman = new Person ('Abdelrahman', 2001);
const Abdelrahman = new Person ('Habiba', 2003);
Enter fullscreen mode Exit fullscreen mode

When we invoke a constructor function, the following do happen:

  • An empty object is created (empty object means that it has neither properties nor methods).

We can add some properties for that empty object in the constructor function’s body using the this keyword (just like we did in the previous example).

  • The this keyword is set to point to the newly created empty object.

  • The newly created object is linked to a prototype, which means:

    A- Creating a new __proto__ property for the object.

    B- Set it to the prototype property of the constructor function.

  • This empty object is returned from the constructor function.

If that seems overwhelming to you, don't worry! Just stick with me, and you will understand everything when I start talking about prototypes.

The need for Prototypes

Suppose that we want to add some methods to the object.

It’s a bad practice to add them inside the constructor function body, as those methods will be shared with all instances, while we don’t always need the methods to be shared. That will affect performance!

Example:

const Person = function (firstName, birthYear) {
    this.firstName = firstName;
    this.birthYear = birthYear;

    // Bad Practice (methods inside constructor function)
    this.calcAge = function () {
        console.log(2037 - this.birthYear);
    }
}
Enter fullscreen mode Exit fullscreen mode

The solution to the previous problem is using prototypes.

Prototypes

Each and every function (including the constructor function) in JS has a property called: prototype.

When we add methods (or properties) to the prototype property of our constructor function, only one copy of this method is made, to be used later by all of the instances.

All the objects (instances) will inherit all the methods defined in the prototype property. This is called prototypal inheritance.

When we call a property or a method on an object, if it’s not found in the object itself, JS will search on its prototype.

The following example shows, how can we add a method to the prototype property of the function constructor:

Person.prototype.calcAge = function () {
    console.log(2037 - this.birthYear);
}

Abdelrahman.calcAge ();
Enter fullscreen mode Exit fullscreen mode

Any object always has access to the methods and properties from its prototype. To do that, we use the special property __proto__ that is available for all JS objects.

Remember: __proto__ used to access the prototype for any object. The prototype property is for the constructor function.

Constructor function’s prototype property is not used to yield the prototype for the constructor function itself, but to yield the prototype for all the objects created from this constructor.

Note: prototype is a property, but constructorFunction.prototype is an object.

Example to illustrate the previous statements:

console.log(Abdelrahman.__proto__ === Person.prototype);  // true
console.log(Person.prototype.isPrototypeOf (Abdelrahman)); // true
console.log(Person.prototype.isPrototypeOf (Person));   // false
Enter fullscreen mode Exit fullscreen mode

Note: We used the isPrototypeOf method (for the prototype property) to check whether the constructor function’s prototype is the prototype of the object passed or not.

Adding properties to the Prototype of the Constructor function

This is not practical in many cases, as all of the instances will share the same value for this property.

Person.prototype.species = "Homo Species";
console.log(Abdelrahman.species, habiba.species);  // Homo Species Homo Species
Enter fullscreen mode Exit fullscreen mode

We can use the hasOwnProerty method for any object and pass an object property (as a string) to it. It will return true if the passed property is not a prototypal property.

console.log(Abdelrahman.hasOwnProperty ('species'));   // false
Enter fullscreen mode Exit fullscreen mode

Prototype Chain

It's a series of links between objects linked using prototypes.

Each created object in JS is just an instance of the Object constructor function.

When we use the curlies {} to write an object literal, it’s equivalent to writing new Object constructor.

The prototype of any object is the constructor function that this object was created from. When we reach Object, it’s on the top of the prototype chain, and it has no parent, so its prototype will be null.

console.log(Abdelrahman.__proto__.__proto__);  // Object.prototype
console.log(Abdelrahman.__proto__.__proto__.__proto__);    // null
Enter fullscreen mode Exit fullscreen mode

Applying what we learned

We can apply what we learned in the Array constructor.

Creating an array using the brackets [], is equivalent to creating it using the new Array constructor.

const arr = [1, 3, 1, 3, 6, 6, 5, 6, 1];
console.log(arr.__proto__ === Array.prototype); // true
Enter fullscreen mode Exit fullscreen mode

We can add a method to the Array constructor function prototype (and all array objects will inherit it).

const arr = [1, 3, 1, 3, 6, 6, 5, 6, 1];
Array.prototype.unique = function () {
    return [...new Set (this)];
};
console.log(arr.unique ()); // Array(4) [ 1, 3, 6, 5 ]
Enter fullscreen mode Exit fullscreen mode

That's it for today! I hope you learned from it. See you soon!

Top comments (1)

Collapse
 
iannyfarue profile image
Ian

Nice article thanks @Tactful-AI