Prototypal inheritance is one of JavaScript's core concepts, It is essential to understand how it works.

The Family Tree

In JavaScript, every object has a hidden [[Prototype]] property that links it to another object. This is like a child inheriting traits from a parent. When you try to access a property or method on an object, JavaScript will look for it on the object itself. If it doesn’t find it it will check the object’s prototype, and so on, up the chain.

const parent = {
  greet() {
    console.log("Hello from the parent!");
  },
};

const child = Object.create(parent);
child.greet(); // Logs: "Hello from the parent!"

In this example, child doesn’t have its own greet method, but it inherits it from parent. This is prototypal inheritance in action.

The ECMAScript Spec

According to the ECMAScript specification, the [[Prototype]] property is what enables this inheritance. When you create an object using Object.create(), you’re explicitly setting its prototype. If you don’t specify a prototype, the object will inherit from Object.prototype by default.

Let’s say you’re building a game with different types of characters. You can use prototypal inheritance to share common properties and methods.

const character = {
  health: 100,
  attack() {
    console.log(`${this.name} attacks for ${this.damage} damage!`);
  },
};

const warrior = Object.create(character);
warrior.name = "Conan";
warrior.damage = 20;

const mage = Object.create(character);
mage.name = "Gandalf";
mage.damage = 15;

warrior.attack(); // Logs: "Conan attacks for 20 damage!"
mage.attack(); // Logs: "Gandalf attacks for 15 damage!"

Here, both warrior and mage inherit the attack method from character, but they have their own unique properties like name and damage.

The Chain

The prototype chain is like a ladder. When you try to access a property or method, JavaScript climbs up the ladder until it finds what it’s looking for or reaches the top (which is null). If it doesn’t find the property, it returns undefined.

For example:

console.log(warrior.health); // 100 (inherited from character)
console.log(warrior.mana); // undefined (not found in the chain)

Why Prototypal Inheritance?

Let's say where going to create a function that returns an object.

function createCar(carName) {
  return {
    name: carName,
    getName() {
      return this.name;
    },
  };
}

const car1 = createCar("Tesla");
const car2 = createCar("Ford");

At first glance, this approach might seem acceptable. However, if we create multiple car objects, each will have its own copy of the getName method. While the properties are unique to each object, duplicating the method for every instance is inefficient since the method logic remains the same across all objects. Ideally, methods should be shared among instances to avoid unnecessary duplication.

TLDR: every car object created has its own copy of the getName method, which wastes memory.

To avoid duplication, we can use prototypal inheritance to share methods between objects. Here's a cleaner implementation using a constructor function and prototype:

function Car(carName) {
  this.name = carName;
}

Car.prototype.getName = function () {
  return this.name;
};

const car1 = new Car("Tesla");
const car2 = new Car("Ford");

console.log(car1.getName()); // Tesla
console.log(car2.getName()); // Ford

Now the getName method is shared between all Car instances. This way, we avoid duplicating the method for each object, making our code more memory-efficient.

Congratulations! Now you know what actually happens when you use the class syntax in JavaScript. Under the hood, JavaScript uses prototypal inheritance to link the prototype of the Car class to its methods. Here's the same example using the class syntax:

class Car {
  constructor(carName) {
    this.name = carName;
  }

  getName() {
    return this.name;
  }
}

const car1 = new Car("Tesla");
const car2 = new Car("Ford");

console.log(car1.getName()); // Tesla
console.log(car2.getName()); // Ford

Prototypal inheritance is a core concept in JavaScript that helps you write efficient and maintainable code. By sharing methods via the prototype chain (or using class), you avoid duplication and keep your code clean. Whether you use constructor functions, Object.create, or class, the goal is the same: share behavior, not memory.

Author Of article : mmvergara Read full article