DEV Community

Cover image for Understanding Prototype Inheritance and ES6 Classes in JavaScript
Jack Pritom Soren
Jack Pritom Soren

Posted on

Understanding Prototype Inheritance and ES6 Classes in JavaScript

JavaScript has a different inheritance mechanism than most conventional OOP languages. Prototypes are the main focus, whereas ES6 classes offer a more contemporary method. Let's examine how ES6 classes improve readability and usefulness as well as how prototype inheritance operates.


1. Prototype: The Foundation of Inheritance

Every object in JavaScript has an internal link to another object called its prototype. This prototype object can have its own prototype, forming a chain.

Example:

const animal = { eats: true };
const rabbit = Object.create(animal);
rabbit.hops = true;

console.log(rabbit.eats); // true (inherited)
console.log(rabbit.hops); // true (own property)
Enter fullscreen mode Exit fullscreen mode

Explanation:

Here, rabbit inherits eats from animal. This demonstrates how objects can share properties through inheritance.


2. Constructor Functions: Building Objects

Before ES6 classes, JavaScript used constructor functions to create objects and initialize their properties.

Example:

function Animal(name) {
  this.name = name;
}
Animal.prototype.eats = true;

const dog = new Animal('Dog');
console.log(dog.name); // Dog
console.log(dog.eats); // true
Enter fullscreen mode Exit fullscreen mode

Explanation:

The Animal constructor initializes name. The eats property is added through the Animal.prototype, enabling inheritance.


3. Master Object: The Common Ancestor

A master object serves as the prototype for other objects.

Example:

const masterObject = { type: 'Generic' };
const specificObject = Object.create(masterObject);
specificObject.name = 'Specific';

console.log(specificObject.type); // Generic (inherited)
console.log(specificObject.name); // Specific (own property)
Enter fullscreen mode Exit fullscreen mode

Explanation:

masterObject is the common ancestor, and specificObject inherits its type property while adding name.


4. Prototype Chain: Following the Hierarchy

JavaScript looks up the prototype chain to find properties and methods.

Example:

const grandparent = { role: 'grandparent' };
const parent = Object.create(grandparent);
parent.role = 'parent';

const child = Object.create(parent);
console.log(child.role); // parent
Enter fullscreen mode Exit fullscreen mode

Explanation:

The child object looks for role. It finds parent's role, demonstrating how the prototype chain resolves property lookups.


5. Prototype Inheritance: Sharing Methods

Objects can share methods through prototype inheritance.

Example:

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a noise.');
};

function Dog(name) {
  Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
  console.log(this.name + ' barks.');
};

const dog = new Dog('Rex');
dog.speak(); // Rex makes a noise.
dog.bark();  // Rex barks.
Enter fullscreen mode Exit fullscreen mode

Explanation:

Dog inherits from Animal, allowing it to access speak. It also defines its own bark method.


6. ES6 Classes: A Cleaner Syntax

ES6 introduced a cleaner, more intuitive way to create classes.

Example:

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

This class-based syntax simplifies the creation and inheritance of objects, making the code more readable.


7. Getters and Setters: Managing Properties

ES6 allows defining methods to access or modify object properties dynamically.

Example:

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  get area() {
    return this.width * this.height;
  }
  set area(value) {
    this.width = Math.sqrt(value);
    this.height = Math.sqrt(value);
  }
}

const rect = new Rectangle(10, 20);
console.log(rect.area); // 200
rect.area = 100;
console.log(rect.width); // 10
console.log(rect.height); // 10
Enter fullscreen mode Exit fullscreen mode

Explanation:

area is a computed property using a getter and setter, allowing dynamic updates.


8. Static Methods: Utility on the Class Level

Static methods belong to the class itself and not to instances.

Example:

class MathHelper {
  static add(a, b) {
    return a + b;
  }
}

console.log(MathHelper.add(2, 3)); // 5
Enter fullscreen mode Exit fullscreen mode

Explanation:

add is a static method accessible directly on MathHelper, useful for utility functions.


9. Polymorphism: Overriding Methods

Polymorphism allows subclasses to redefine methods from the parent class.

Example:

class Animal {
  speak() {
    console.log('Animal makes a noise.');
  }
}
class Dog extends Animal {
  speak() {
    console.log('Dog barks.');
  }
}

const myPet = new Dog();
myPet.speak(); // Dog barks.
Enter fullscreen mode Exit fullscreen mode

Explanation:

Dog overrides speak from Animal, providing its own implementation.


Conclusion

The foundation of JavaScript object-oriented programming is made up of ES6 classes and prototype inheritance. Writing reusable, maintainable code is improved by knowing how to use constructor functions, prototypes, and ES6 classes. To fully utilize JavaScript's inheritance paradigm, embrace these ideas!

Follow me on : Github Linkedin

Top comments (1)

Collapse
 
philip_zhang_854092d88473 profile image
Philip

Thanks for the detail explanation! The transition enhances code readability and structure for better maintainability. EchoAPI can assist by streamlining API testing for methods that use inheritance, helping developers test and mock APIs more efficiently during development.