Javascript Prototypes
Table of Contents
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 methodspeak
.Dog
is another constructor function intended to inherit fromAnimal
.- We set
Dog.prototype
to an instance ofAnimal
, and then reset theconstructor
property. - A new method
bark
is added toDog
.
Prototype Chain #
When you access a property of d
, such as d.speak
, the JavaScript engine will:
- Check if
d
has aspeak
property. - If not found, check
Dog.prototype
. - If not found, check
Animal.prototype
, where it findsspeak
.
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.