Skip to main content

Javascript Prototypes

·3 mins

JavaScript has many factors that leads to confusion when one starts to learn it. Maybe the biggest offender is is defining prototypal inheritance. The confusion is that it is very close to classical inheritance, but could be described as rather being delegation than inheritance.

While classes were introduced in ECMAScript 2015 and can be used incases where inheritance is needed. It is important to note that it is just syntax sugar for prototypes and it still behooves you to learn the inner workings. I will do my best to simple provide an overview of how they work.

Basic Concept of Prototypal Inheritance #

In JavaScript, every object can inherit properties and methods from another object, known as its prototype. This prototype is itself an object, thus having it’s own prototype, creating a prototype chain. The chain’s base end a prototype is reached and it’s prototype is null.

When you try to access a property of an object, the object checks itself and if the property can’t be found, the object searches it’s prototype the property. If the property still can’t be found, then the prototype’s prototype is searched, and so on until either the property is found, or the end of the chain is reached, in which case undefined is returned.

Example 1: Using Object.create() #

const animal = {
  isAlive: true
};

// Creating a new object 'dog' that inherits from 'animal'
const dog = Object.create(animal);
console.log(dog.isAlive); // true

In this example, dog inherits the property isAlive from animal. This is because animal is set as the prototype of dog.

Example 2: Using Constructor Functions #

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function () {
  console.log(`${this.name} makes a sound.`);
};

function Dog(name) {
  Animal.call(this, name); // call super constructor.
}

// Setting Dog's prototype to be an instance of Animal
Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.bark = function () {
  console.log(`${this.name} barks.`);
};

const d = new Dog('Mitzie');
d.speak(); // Mitzie makes a sound.
d.bark();  // Mitzie barks.

In this example:

  • Animal is a constructor function with a method speak.
  • Dog is another constructor function intended to inherit from Animal.
  • We set Dog.prototype to an instance of Animal, and then reset the constructor property.
  • A new method bark is added to Dog.

Prototype Chain #

When you access a property of d, such as d.speak, the JavaScript engine will:

  1. Check if d has a speak property.
  2. If not found, check Dog.prototype.
  3. If not found, check Animal.prototype, where it finds speak.

This chain continues until the property is found or the end of the chain is reached.

Modifying Prototypes #

Animal.prototype.eat = function () {
  console.log(`${this.name} eats.`);
};

d.eat(); // Mitzie eats.

By adding a new method eat to Animal.prototype, all instances of Animal and its derived instances (like Dog) gain access to the eat method.

Benefits and Considerations #

  • Reusability: Code can be reused across different objects.
  • Flexibility: It’s easy to extend objects with new functionality.
  • Memory Efficiency: Shared properties/methods are stored only once in memory.