Prototypes & Inheritance Get Certified
Almost everything in JavaScript is an object, and every object has a hidden link to another object called its prototype. Understanding the prototype chain is the key to understanding inheritance in JavaScript.
What Is a Prototype?
When you try to access a property that does not exist on an object, JavaScript looks for it on the object’s prototype, then on that prototype’s prototype, and so on — this is the prototype chain.
What this code does: Creates a simple object and shows that it
inherits methods from Object.prototype.
const person = {
name: "Alice"
};
console.log(person.name);
console.log(person.toString()); // inherited from Object.prototype
console.log(Object.getPrototypeOf(person) === Object.prototype);
const person = { name: "Alice" } — only has one own property.console.log(person.name) — found directly on the object.console.log(person.toString()) — not defined on person; JavaScript walks up to Object.prototype and finds it there.Object.getPrototypeOf(person) === Object.prototype — every plain object’s prototype is Object.prototype by default.Constructor Functions and the Prototype
When you create objects with a constructor function and the
new keyword, methods should be placed on the
constructor’s prototype so they are shared by all
instances.
What this code does: Defines a constructor and adds a method to its prototype. Every instance shares the same method.
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return this.name + " makes a sound.";
};
const dog = new Animal("Rex");
const cat = new Animal("Whiskers");
console.log(dog.speak());
console.log(cat.speak());
console.log(dog.speak === cat.speak); // true – same shared function
function Animal(name) — called with new to create instances.this.name = name — each animal gets its own name.}Animal.prototype.speak = function () { ... } — one function shared by every instance; saves memory versus defining it inside the constructor.const dog = new Animal("Rex")const cat = new Animal("Whiskers")console.log(dog.speak())console.log(cat.speak())console.log(dog.speak === cat.speak) — true because both look up the same function on Animal.prototype.Object.create()
Object.create() creates a new object with the
specified object as its prototype, without needing a
constructor function at all.
What this code does: Uses Object.create() to make a new
object whose prototype is an existing object, then calls an
inherited method.
const animal = {
speak: function () {
return this.name + " makes a sound.";
}
};
const dog = Object.create(animal);
dog.name = "Rex";
console.log(dog.speak());
console.log(Object.getPrototypeOf(dog) === animal);
const animal = { speak: function () { ... } } — will serve as the prototype for other objects.const dog = Object.create(animal) — dog’s internal [[Prototype]] is animal; no constructor needed.dog.name = "Rex"console.log(dog.speak()) — found on animal via the prototype chain.Object.getPrototypeOf(dog) === animalInheritance with Constructor Functions
Before class syntax, one constructor function could
inherit from another by linking their prototypes together with
Object.create().
What this code does: Implements inheritance between constructor functions by linking prototypes and calling the parent constructor.
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return this.name + " makes a sound.";
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
return this.name + " barks.";
};
const rex = new Dog("Rex");
console.log(rex.speak());
console.log(rex.bark());
function Animal(name) { this.name = name; }function Dog(name)Animal.call(this, name) — sets name on the new Dog instance (pre-class inheritance pattern).}Dog.prototype = Object.create(Animal.prototype) — Dog instances will find Animal methods via the chain.Dog.prototype.constructor = Dog — Object.create overwrites constructor; restore it so instanceof and constructor checks work.Dog.prototype.bark = function () { ... }const rex = new Dog("Rex")console.log(rex.speak())console.log(rex.bark())ES6 Classes
The class keyword is syntactic sugar over the same
prototype system. It provides a cleaner syntax for creating
constructor functions and prototype methods.
What this code does: Introduces modern class syntax.
Under the hood the class still creates a constructor and places
methods on its prototype.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " makes a sound.";
}
}
const cat = new Animal("Whiskers");
console.log(cat.speak());
console.log(typeof Animal); // "function"
class Animal — syntactic sugar over the same prototype system.constructor(name) { this.name = name; } — runs when you use new.speak() { ... } — placed on Animal.prototype automatically, shared by all instances.}const cat = new Animal("Whiskers")console.log(cat.speak())console.log(typeof Animal) — “function”; classes are constructor functions under the hood.Inheritance with extends and super
The extends keyword creates inheritance between
classes, and super() calls the parent class’s
constructor.
What this code does: Shows a subclass that extends a parent class,
calls super(), and inherits the parent’s methods.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " makes a sound.";
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
return this.name + " barks.";
}
}
const rex = new Dog("Rex", "Labrador");
console.log(rex.speak());
console.log(rex.bark());
console.log(rex.breed);
class Dog extends Animal — Dog.prototype’s prototype becomes Animal.prototype.constructor(name, breed) {super(name) — calls Animal’s constructor; required before using this.this.breed = breed}bark() { ... }}const rex = new Dog("Rex", "Labrador")console.log(rex.speak())console.log(rex.bark())console.log(rex.breed)Overriding Methods with super
A subclass can override a parent method and still call the
original version using super.methodName().
What this code does: Overrides a method in the child class and uses
super.speak() to invoke the parent version.
class Animal {
speak() {
return "Some generic sound";
}
}
class Dog extends Animal {
speak() {
const base = super.speak();
return base + ", but specifically a bark";
}
}
const rex = new Dog();
console.log(rex.speak());
speak() { return "Some generic sound"; }class Dog extends Animalspeak() { — same method name shadows the parent version for Dog instances.const base = super.speak() — super reaches the parent method without hard-coding the parent name.return base + ", but specifically a bark"} }const rex = new Dog()console.log(rex.speak())instanceof and the Prototype Chain
The instanceof operator checks whether an object’s
prototype chain includes a given constructor’s
prototype.
What this code does: Uses instanceof to check an
object’s place in the prototype chain.
class Animal {}
class Dog extends Animal {}
const rex = new Dog();
console.log(rex instanceof Dog); // true
console.log(rex instanceof Animal); // true
console.log(rex instanceof Object); // true
class Animal {}class Dog extends Animal {}const rex = new Dog()rex instanceof Dog — true: Dog.prototype is in rex’s chain.rex instanceof Animal — true: Animal.prototype is further up the chain.rex instanceof Object — true: everything ultimately links to Object.prototype.Own Properties vs. Inherited Properties
hasOwnProperty() checks whether a property exists
directly on the object itself, rather than somewhere up the
prototype chain.
What this code does: Distinguishes own properties from inherited
methods using hasOwnProperty().
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " makes a sound.";
}
}
const rex = new Animal("Rex");
console.log(rex.hasOwnProperty("name")); // true
console.log(rex.hasOwnProperty("speak")); // false
const rex = new Animal("Rex")rex.hasOwnProperty("name") — true: name was assigned on the instance itself.rex.hasOwnProperty("speak") — false: speak lives on Animal.prototype, not on rex. hasOwnProperty does not walk the chain.Constructor Functions vs. Classes vs. Object.create()
| Feature | Constructor Function | ES6 Class | Object.create() |
|---|---|---|---|
| Requires new keyword? | Yes | Yes | No |
| Inheritance syntax | Object.create(Parent.prototype) | extends / super | Pass parent object directly |
| Under the hood | Prototypes | Prototypes | Prototypes |
| Common in modern code? | Rarely (legacy) | Yes | Sometimes |
Common Mistakes with Prototypes
- Defining methods inside the constructor instead of on the prototype, wasting memory.
- Forgetting to reset
Dog.prototype.constructorafter reassigning the prototype. - Forgetting to call
super()before usingthisinside a subclass constructor. - Modifying built-in prototypes such as
Array.prototype. - Confusing
__proto__with the constructor’sprototypeproperty.
Best Practices for Prototypes & Inheritance
- Prefer
classsyntax for new code — it is clearer and less error-prone. - Put shared methods on the prototype (or inside a
classbody), not inside the constructor. - Always call
super()first in a subclass constructor before accessingthis. - Avoid modifying the prototypes of built-in objects like
ArrayorObject. - Prefer composition over deep inheritance chains when objects do not truly share an “is-a” relationship.
Quick Summary
- Every object has a prototype it can fall back on for properties and methods it does not have itself.
- Property lookups travel up the prototype chain until they are found or the chain ends.
- Methods placed on a constructor’s
prototypeare shared by every instance. Object.create()creates a new object with a specific prototype directly.class,extends, andsuperare modern syntax built on top of the same prototype system.instanceofchecks whether a constructor’s prototype appears in an object’s prototype chain.hasOwnProperty()distinguishes an object’s own properties from ones it inherited.
Try It Yourself
Modify the classes below. Try adding a new method, overriding
speak() in Cat, or checking
instanceof against different classes.
What this code does: A complete practice example combining class
inheritance, method overriding with super,
instanceof, and hasOwnProperty().
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " makes a sound.";
}
}
class Cat extends Animal {
speak() {
return super.speak() + " Specifically, a meow.";
}
}
const whiskers = new Cat("Whiskers");
console.log(whiskers.speak());
console.log(whiskers instanceof Animal);
console.log(whiskers.hasOwnProperty("name"));
console.log(whiskers.hasOwnProperty("speak"));
class Cat extends Animalspeak() {return super.speak() + " Specifically, a meow."} }const whiskers = new Cat("Whiskers")console.log(whiskers.speak())console.log(whiskers instanceof Animal) — true via the prototype chain.console.log(whiskers.hasOwnProperty("name")) — true.console.log(whiskers.hasOwnProperty("speak")) — false; speak is on Cat.prototype (and the override still is not an own property of the instance).Tip: Under the hood,classsyntax, constructor functions, andObject.create()all rely on the same prototype chain — learning to read that chain makes it much easier to understand how inheritance really works in JavaScript.
Understanding prototypes and inheritance explains what happens
behind the scenes of every class you write, and
prepares you for more advanced topics like mixins, the
this keyword, and closures.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.