Object-Oriented JavaScript: Prototypes, Classes & Inheritance
How to model real things — users, products, animals — as reusable blueprints. Classes, the prototype chain behind them, and inheritance: a full mandatory section in every paid course.
What you will learn
- Create objects from a class blueprint
- Explain the prototype chain in plain words
- Reuse code with extends (inheritance) and encapsulation
A class is a blueprint for objects
So far you have written objects one at a time with { }. But what if you need hundreds of users, each with the same shape and the same methods? A class is a blueprint (think cookie cutter) that stamps out as many objects (cookies) as you like, each with its own data but sharing the same behaviour.
You define a class with the class keyword. The special constructor method sets up each new object’s starting data, and this refers to the specific object being built. You create an object from the class with the new keyword.
<script>
class Dog {
constructor(name, breed) {
this.name = name; // each dog gets its own name
this.breed = breed;
}
bark() {
return this.name + " says Woof!";
}
}
const d1 = new Dog("Bruno", "Labrador");
const d2 = new Dog("Lucy", "Beagle");
document.write(d1.bark() + "<br>");
document.write(d2.bark());
</script>Step by step:
class Dogdefines the blueprint: what data each dog holds and what it can do.new Dog("Bruno", "Labrador")stamps out a new dog object; theconstructorruns, settingthis.nameandthis.breedfor that dog.- We build a second dog,
d2, with its own separate name and breed. - Each object has the shared
bark()method, butthis.nameinside it refers to the specific dog, sod1.bark()andd2.bark()differ.
Note: Output: Bruno says Woof! Lucy says Woof!
Watch out: Always use new to create an object from a class. Calling Dog("Bruno") without new throws a TypeError, because the constructor needs new to build a fresh object and set its this.
The prototype chain — where methods really live
Here is what is happening underneath. The bark method is not copied into every dog — that would waste memory. Instead, all dogs share one copy, stored on the class’s prototype. When you call d1.bark(), JavaScript first looks on the d1 object itself; not finding bark there, it follows a link up to Dog’s prototype and finds it. That hop-up-the-links search is the prototype chain.
<script>
class Dog {
constructor(name) { this.name = name; }
bark() { return "Woof"; }
}
const d = new Dog("Bruno");
// bark is NOT on the object itself — it is found via the prototype
document.write("Has own 'name'? " + d.hasOwnProperty("name") + "<br>");
document.write("Has own 'bark'? " + d.hasOwnProperty("bark") + "<br>");
document.write("Can it bark? " + d.bark());
</script>hasOwnProperty asks "is this directly on the object?". name is — it was set in the constructor for this specific dog. But bark is not on the object itself; it lives on Dog’s prototype, shared by all dogs. Yet d.bark() still works, because JavaScript follows the prototype chain up to find it. This is prototypal inheritance — the real engine under the class syntax.
Note: Output: Has own 'name'? true Has own 'bark'? false Can it bark? Woof
Tip: Plain-words summary: every object has a hidden link to a "parent" object (its prototype). Looking up a property checks the object first, then walks up these links until it is found or the chain ends. Classes are a clean, familiar syntax sitting on top of this mechanism.
Inheritance — build on an existing class
Inheritance lets one class build on another: a child class gets everything the parent has, and can add or override behaviour. Use extends to inherit, and super(...) inside the child constructor to run the parent’s setup first.
<script>
class Animal {
constructor(name) { this.name = name; }
eat() { return this.name + " is eating."; }
}
class Cat extends Animal { // Cat inherits from Animal
constructor(name, colour) {
super(name); // run Animal's constructor first
this.colour = colour;
}
meow() { return this.name + " says Meow!"; }
}
const c = new Cat("Misty", "grey");
document.write(c.eat() + "<br>"); // inherited from Animal
document.write(c.meow()); // Cat's own method
</script>Walk through the inheritance:
Animalis the parent: it holds anameand aneat()method.class Cat extends AnimalmakesCata child that inherits everything fromAnimal.- In
Cat’s constructor,super(name)callsAnimal’s constructor to set upname— you must do this before usingthis. Catthen adds its own data (colour) and its own method (meow).- So
c.eat()works (inherited fromAnimal) andc.meow()works (added byCat) — code reused, then extended.
Note: Output: Misty is eating. Misty says Meow!
Watch out: In a subclass constructor you must call super() before using this. Forgetting it throws "Must call super constructor before accessing this" — a very common beginner error with inheritance.
Encapsulation — private fields
Encapsulation means hiding an object’s internal data so it can only be changed through controlled methods. Modern classes support truly private fields by prefixing the name with # — code outside the class cannot read or change them.
<script>
class Account {
#balance = 0; // private — only this class can touch it
deposit(amount) { this.#balance += amount; return this.#balance; }
getBalance() { return this.#balance; }
}
const acc = new Account();
acc.deposit(500);
acc.deposit(200);
document.write("Balance: " + acc.getBalance());
// acc.#balance would be a SyntaxError — it is private
</script>#balance is a private field: the only way to change it is through the deposit method, and the only way to read it is getBalance. Outside code cannot reach acc.#balance directly — that protects your data from accidental or unwanted changes. This is encapsulation: a clean public interface around hidden internals.
Note: Output: Balance: 700
Tip: OOP in one breath: a class is a blueprint, new stamps out objects, methods live on the prototype (shared), extends/super give inheritance, and #fields give encapsulation. React class components, Node libraries and most interview questions build on exactly these ideas.
Q. In a subclass constructor that uses extends, what must you call before using this?
✍️ Practice
- Write a
Bookclass with a constructor and adescribe()method, then create two books. - Make a
Studentclass thatextendsaPersonclass, callingsuperand adding one new method. - Add a
#private field to a class and prove it cannot be read from outside.
🏠 Homework
- Model a small system with inheritance — for example a
Shapeparent andCircle/Rectanglechildren, each with its ownarea()method.