DEV Community

Hridayesh Sharma
Hridayesh Sharma

Posted on • Updated on

Understanding Classes (ES5) and Prototypal Inheritance in JavaScript

function Person(name, age) {
    this.name = name;
    this.age = age;
}

const me = new Person('Joe', 20);

console.log(me); // {name: 'Joe', age: 20}

In a nutshell the above snippet creates a Person class that can have multiple instances. By convention functional classes in javascript start with a capital letter.

Let's dig deeper into what's going on here?? How a normal function can be used for classess?? 🤔🤔

The Person function is just like any other function which when called directly just returns undefined as we are not explicitly returning anything from it. But the real magic lies in the new keyword on line var me = new Person('Joe', 20).
Lets understand that magic - There are a couple of steps that happen when we use the new keyword to initialize a function :

  1. An empty object {} is created.
  2. Person is called by passing the reference of the object to it: Person.call({}, 'Joe', 20)
  3. Inside Person this now refers to the object passed in the above step.
  4. The object's proto is set to the function's prototype using {}.__proto__ = Person.prototype.
  5. Finally return the object and that's what we get into me

Concept Aside: Every function in javascript has a prototype object available on it. That's how you use Array.prototype.map. And every object has a __proto__ object on it. For further watch this great video Discovering JavaScript.

Since the prototype and __proto__ refer to the same object, whenever you add a new function on the prototype it becomes available on all instances.

Person.prototype.greet = function() {
  console.log('Hi', this.name);
}

me.greet(); // Hi Joe

const you = new Person('Alice', 22);
you.greet(); // Hi Alice

So far we undestood how classes are created in javascript. Let's understand how to inherit classes in javascript.

Let's create a new class called Employee that inherits the Person class

function Employee(name, age, title) {
  Person.call(this, name, age);
  this.title = title;
}

// create Employee prototype from Person prototype
Employee.prototype = Object.create(Person.prototype);

const joe = new Employee('Joe', 22, 'Developer');
console.log(joe.name); // Joe
joe.greet(); // Hi Joe

Woah, we have finally inherited our Person class to create an Employee class and we didn't have to rewrite the greet function.

Let's see what just happened ??

  1. We created our Employee class just like we created Person class.
    • Inside our employee class we are calling the Person class by passing it this reference. This is just like using super keyword in ES6 classes.
  2. This is the most important part. We are recreating the Employee prototype from Person's prototype to get access to all the methods available on the Person class.

Now you may ask yourself Why use Object.create and not just assign the Person prototype to Employee.
This is because we don't want Person and Employee to share the same prototype as objects in javascript are referenced. That's the whole point of inheriting Person.

So that's how we use prototypal inheritance in javascript. The new ES6 classes are basically a syntactic sugar on top of it. Basically this is what actually happens under the hood.

PS: You can find the complete code here GitHub Repo

Top comments (7)

Collapse
 
xgqfrms profile image
xgqfrms • Edited

function Person(name, age) {
    this.name = name;
    this.age = age;
}
// undefined

Person.__proto__;
// ƒ () { [native code] }
Person.prototype;
// {constructor: ƒ}constructor: ƒ Person(name, age)arguments: nullcaller: nulllength: 2name: "Person"prototype: {constructor: ƒ}[[FunctionLocation]]: VM92:1[[Prototype]]: ƒ ()[[Scopes]]: Scopes[2][[Prototype]]: Object

const me = new Person('Eric', 18);
// undefined
me.__proto__;
// {constructor: ƒ}constructor: ƒ Person(name, age)arguments: nullcaller: nulllength: 2name: "Person"prototype: constructor: ƒ Person(name, age)arguments: nullcaller: nulllength: 2name: "Person"prototype: constructor: ƒ Person(name, age)[[Prototype]]: Object[[FunctionLocation]]: VM92:1[[Prototype]]: ƒ ()[[Scopes]]: Scopes[2][[Prototype]]: Object[[FunctionLocation]]: VM92:1[[Prototype]]: ƒ ()[[Scopes]]: Scopes[2][[Prototype]]: Object
me.prototype;
// undefined

me.__proto__ === Person.prototype;
// true

Enter fullscreen mode Exit fullscreen mode
Collapse
 
aminnairi profile image
Amin

Hi there, great article. You can host your code on Repl.it to let people run the code online. This is awesome for a quick preview of the code without having to copy and paste manually. Plus they can play with it easily and run the code again. I mentioned one but there are tons of online code hosting.

Collapse
 
vyasriday profile image
Hridayesh Sharma

Hi thanks for appreciation. I have this code available in my GitHub repo. I will provide a link to it. And yeah I think I can add a repl next time.

Collapse
 
ayaanraj profile image
ayaanraj

Hey Hridayesh, great article.
Simple and awesome explanation of Prototype Inheritance.
keep it up.

Collapse
 
vyasriday profile image
Hridayesh Sharma

Hey ayaan, thanks for appreciation.

Collapse
 
irochkaz profile image
Iryna Zaletko

thsnk U, cool and the main thing is clearly explained

Collapse
 
vyasriday profile image
Hridayesh Sharma

Thanks Iryna.