JavaScript’s object system is unique compared to many classical languages because it is based on prototypical inheritance. Unlike classical inheritance where classes inherit from other classes, JavaScript objects inherit directly from other objects through a prototype chain. This model allows objects to share properties and methods by linking to a prototype object, which can then link to another prototype, and so forth. Understanding prototypical inheritance is essential to mastering JavaScript’s flexible and powerful object system.
with hands-on learning.
get the skills and confidence to land your next move.
In this article, we will explore how to use prototypical inheritance in JavaScript by focusing on practical “how-to” examples. You will learn to create objects that inherit properties and methods from other objects, how to override inherited behavior, and how prototype chains work behind the scenes. The examples are designed to be fun and engaging, helping you see inheritance in action with relatable and creative code.
Creating an Object with a Prototype Using Object.create()
One of the simplest ways to set up prototypical inheritance in JavaScript is by using the built-in Object.create() method. This method creates a new object, setting the prototype of that object to another object you specify.
Let’s start by creating a basic object named Car. This Car object will have some properties such as make and year. Then, we will create a new object named Tesla that inherits from Car and adds its own unique property.
const Car = {
make: "Generic",
year: 2000,
startEngine() {
return `The ${this.make} engine has started.`;
}
};
const Tesla = Object.create(Car);
Tesla.make = "Tesla";
Tesla.year = 2025;
Tesla.batteryLife = "500 miles";
console.log(Tesla.startEngine()); // Inherited method
console.log(`Battery life: ${Tesla.batteryLife}`); // Own propertyHere, Tesla is created with Car as its prototype. This means Tesla inherits the startEngine method from Car. We override the make and year properties to reflect Tesla’s identity and add a new property, batteryLife. When we call Tesla.startEngine(), JavaScript looks up startEngine on Tesla. Since it’s not directly on Tesla, it checks the prototype (Car) and finds it there, allowing Tesla to use the method without redefining it.
Adding Properties and Methods to Prototypes
When working with objects created via constructor functions or Object.create(), you can add shared methods and properties to their prototype so all derived objects share the same behavior without duplicating code.
Suppose we want to create a constructor function CarConstructor and add a shared method to its prototype.
function CarConstructor(make, year) {
this.make = make;
this.year = year;
}
CarConstructor.prototype.startEngine = function () {
return `The ${this.make} engine roars to life!`;
};
const ford = new CarConstructor("Ford", 2018);
console.log(ford.startEngine());In this example, the startEngine method is added to CarConstructor.prototype. Any object created with new CarConstructor() automatically inherits this method. Calling ford.startEngine() works because JavaScript follows the prototype chain to find it.
Overriding Prototype Methods in Child Objects
Sometimes you want to customize or replace a method inherited from a prototype. JavaScript allows you to override prototype methods by simply defining a method with the same name directly on the child object.
Using the previous Car example, let’s override startEngine in a new object Tesla.
const Car = {
make: "Generic",
year: 2000,
startEngine() {
return `The ${this.make} engine has started.`;
}
};
const Tesla2 = Object.create(Car);
Tesla2.make = "Tesla";
Tesla2.startEngine = function () {
return `The ${this.make}'s silent electric engine hums softly.`;
};
console.log(Tesla2.startEngine()); // Overridden methodHere, although Tesla2 inherits from Car, it replaces the inherited startEngine method with its own version. Now, calling Tesla2.startEngine() executes this new method, demonstrating how overrides work.
Using Constructor Functions and Prototypes for Inheritance
Before ES6 classes, constructor functions paired with prototypes were the main way to implement inheritance in JavaScript. Constructor functions create new objects, and shared methods are stored on their prototype.
Let’s create an Animal constructor with a shared method.
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return `${this.name} makes a sound.`;
};
const dog = new Animal("Kol");
console.log(dog.speak());This code creates an Animal object named dog. The speak method is shared among all Animal instances, kept on the prototype to avoid duplication.
Setting Up Inheritance Between Constructor Functions
Inheritance between constructor functions requires setting the prototype of the child constructor to an instance of the parent’s prototype. This creates the prototype chain.
Let’s create a Dog constructor that inherits from Animal.
function Dog(name, breed) {
Animal.call(this, name); // Call parent constructor
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
return `${this.name} barks loudly!`;
};
const bulldog = new Dog("Bruno", "Bulldog");
console.log(bulldog.speak()); // Inherited method
console.log(bulldog.bark()); // Child methodHere, Dog calls Animal’s constructor to set name. Then we set Dog.prototype to inherit from Animal.prototype. After that, we fix the constructor property to point back to Dog. Now, bulldog can access both speak from Animal and bark defined on Dog.
Calling Parent Prototype Methods in Child Objects
Sometimes a child method needs to reuse the parent’s method. This can be done by explicitly calling the parent prototype’s method with the child’s context.
In the Dog example, let’s override speak but call the parent method inside it.
Dog.prototype.speak = function () {
const parentSpeak = Animal.prototype.speak.call(this);
return `${parentSpeak} Also, ${this.name} wags its tail.`;
};
console.log(bulldog.speak());This code overrides speak in Dog. It first calls the parent’s speak using call(this) to keep the right context, then adds additional behavior. The output combines both messages, showing method reuse.
Prototype Chain Exploration and Property Lookup
JavaScript looks for properties and methods starting from the object itself, then up the prototype chain until it finds the property or reaches the end (null).
Let’s create a deeper chain: Animal → Dog → Bulldog.
function Bulldog(name) {
Dog.call(this, name, "Bulldog");
}
Bulldog.prototype = Object.create(Dog.prototype);
Bulldog.prototype.constructor = Bulldog;
Bulldog.prototype.snore = function () {
return `${this.name} snores loudly. Zzz!`;
};
const max = new Bulldog("Max");
console.log(max.bark()); // From Dog
console.log(max.speak()); // Overridden in Dog
console.log(max.snore()); // From Bulldogmax has access to methods from all levels. When max.bark() is called, JavaScript looks on max, then Bulldog.prototype, then Dog.prototype and finds bark. This demonstrates how the prototype chain directs property lookup.
Fun Example: Building a Magical Creature Prototype Chain
To illustrate prototypical inheritance in a playful way, let’s create a chain of magical creatures.
const Creature = {
type: "Creature",
describe() {
return `A mysterious ${this.type} wanders the forest.`;
}
};
const Elf = Object.create(Creature);
Elf.type = "Elf";
Elf.castSpell = function () {
return `${this.type} casts a shimmering light spell.`;
};
const HighElf = Object.create(Elf);
HighElf.type = "High Elf";
HighElf.castSpell = function () {
return `${this.type} casts an ancient powerful spell.`;
};
console.log(Creature.describe());
console.log(Elf.describe());
console.log(Elf.castSpell());
console.log(HighElf.describe());
console.log(HighElf.castSpell());Here, Elf inherits from Creature, and HighElf inherits from Elf. Each level can override methods like castSpell to create unique behavior. This example shows how prototypical inheritance can model real-world hierarchies with shared and specialized behaviors.
Conclusion
Prototypical inheritance is the foundation of JavaScript’s object model, allowing objects to inherit properties and methods directly from other objects. Using Object.create(), constructor functions, and prototype chains, you can set up inheritance that shares functionality efficiently while allowing overrides and extensions. Understanding this model helps unlock the full power of JavaScript objects and their dynamic behavior.
By practicing with real examples, such as creating vehicles, animals, or magical creatures, you see prototypical inheritance in action and appreciate its flexibility and expressiveness.
References
If you want to learn more about JavaScript prototypical inheritance and related topics, these resources are excellent starting points:
- MDN Web Docs: Inheritance and the prototype chain
The official documentation explaining inheritance and how prototypes chain together. - MDN Web Docs: Object.create()
Learn aboutObject.create()and how it sets up prototypes. - MDN Web Docs: Constructor functions
A guide to creating objects using constructor functions and prototypes. - JavaScript.info: Prototypal inheritance
A detailed tutorial on prototypes and inheritance in JavaScript.




