The Family Tree Analogy
Think about your family:
- You inherited traits from your parents - maybe your dad's eye color, your mom's height
- But you also have unique traits they don't have
- And you might do some things differently than they do
Inheritance in programming works exactly the same way.
A child class inherits everything from a parent class:
- All the parent's properties (data)
- All the parent's methods (behavior)
Then it can:
- Add its own unique properties and methods
- Change (override) inherited behaviors
Why Do We Need Inheritance?
The Problem: Repeated Code
Imagine coding a zoo simulation without inheritance:
Dog: name, age, eat(), sleep(), bark()
Cat: name, age, eat(), sleep(), meow()
Bird: name, age, eat(), sleep(), fly()
Fish: name, age, eat(), sleep(), swim()
See the repetition? name, age, eat(), sleep() are the same for ALL animals!
The Solution: Inheritance
Create one "Animal" class with the common stuff, then specialize:
Animal: name, age, eat(), sleep()
↓ inherits ↓
Dog: bark() (+ everything from Animal)
Cat: meow() (+ everything from Animal)
Bird: fly() (+ everything from Animal)
Fish: swim() (+ everything from Animal)
Now if you need to change how eat() works, you change it in ONE place!
The Parent-Child Relationship
Think of it as a family tree:
[Parent Class]
Animal
/ \
/ \
[Child Classes]
Dog Cat
The parent is also called:
- Base class (the foundation)
- Super class (the one above)
The child is also called:
- Derived class (derived from parent)
- Sub class (below the parent)
Key rule: The child "IS A" type of parent.
- Dog IS A Animal âś“
- Cat IS A Animal âś“
- Car is not an Animal âś—
How It Works in Code
Step 1: Define the parent class
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(this.name + " is eating");
}
sleep() {
console.log(this.name + " is sleeping");
}
}
Step 2: Create child class using extends
class Dog extends Animal {
// Call parent constructor with super()
constructor(name, breed) {
super(name); // This runs Animal's constructor
this.breed = breed; // Add new property
}
// Add new method (dogs can bark)
bark() {
console.log(this.name + " says woof!");
}
}
Step 3: The child has EVERYTHING
const max = new Dog("Max", "Labrador");
// Inherited from Animal:
max.eat(); // "Max is eating"
max.sleep(); // "Max is sleeping"
// Its own:
max.bark(); // "Max says woof!"
The super Keyword
super lets the child talk to its parent:
In constructor: Call parent's constructor first
constructor(name, breed) {
super(name); // Typically comes first
this.breed = breed;
}
In methods: Call parent's version of a method
speak() {
super.speak(); // Run parent's speak() first
console.log("And also barks!");
}
Overriding: Changing Inherited Behavior
Children can replace inherited methods:
Animal.speak() → "Makes a sound"
Dog overrides with:
Dog.speak() → "Barks"
Cat overrides with:
Cat.speak() → "Meows"
The child's version runs instead of the parent's.
Example: All employees work, but different types work differently:
- Generic Employee: "Working"
- Developer: "Writing code"
- Manager: "In meetings"
Real-World Examples
1. Website Users
User: username, email, login(), logout()
↓
Admin: deleteUser(), banUser() (+ User stuff)
Moderator: editPosts() (+ User stuff)
Member: createPost() (+ User stuff)
2. Game Characters
Character: health, position, move(), takeDamage()
↓
Warrior: sword, slash() (+ Character stuff)
Mage: mana, castSpell() (+ Character stuff)
Archer: arrows, shoot() (+ Character stuff)
3. Shapes
Shape: color, getArea()
↓
Circle: radius, getArea() → πr²
Rectangle: width, height, getArea() → w×h
Triangle: base, height, getArea() → ½bh
Inheritance vs Composition
Not everything should use inheritance. Ask yourself:
"IS-A" → Use Inheritance
- Dog IS A Animal âś“
- Car IS A Vehicle âś“
"HAS-A" → Use Composition
- Car HAS A Engine (not: Car IS A Engine)
- House HAS A Kitchen (not: House IS A Kitchen)
❌ class Car extends Engine // Car is not an engine!
âś“ class Car { engine = new Engine() } // Car has an engine
Rule of thumb: When in doubt, prefer composition. It's more flexible.
Common Mistakes
Mistake 1: Deep Inheritance Chains
❌ Bad:
Animal → Mammal → Canine → Dog → Labrador → YellowLabrador
âś“ Good:
Animal → Dog (keep it shallow!)
Deep chains are confusing and fragile. Try to keep inheritance to 2-3 levels max.
Mistake 2: Forgetting super() in Constructor
class Child extends Parent {
constructor(name) {
// ❌ Forgot super() - ERROR!
this.name = name;
}
}
class Child extends Parent {
constructor(name) {
super(); // âś“ Typically first
this.name = name;
}
}
Mistake 3: Inheriting Mainly for Code Reuse
If there's no real "is-a" relationship, don't use inheritance just to share code. Use utilities or composition instead.
❌ Rectangle extends Logger // Makes no sense!
âś“ Rectangle uses Logger // Better
FAQ
Q: Can a class have multiple parents?
In JavaScript, generally no - it's single inheritance. One class can extend one parent. Some languages (Python, C++) allow multiple inheritance, but it gets complicated.
Q: What's the difference between extends and implements?
extends = inheritance (getting code from parent)
implements = interface (promising to have certain methods)
TypeScript has both. JavaScript typically uses extends for inheritance.
Q: When should I use inheritance?
When there's a TRUE "is-a" relationship with shared behavior. Ask: "Would it make sense to say 'X is a Y'?"
Q: Can I prevent a method from being overridden?
Not in vanilla JavaScript. In TypeScript, you can use final or readonly patterns.
Q: What if I don't want the parent constructor to run?
You generally need to call super() in a child constructor. If you don't want certain parent setup, you might need to rethink your class design.
Q: Is inheritance "bad"?
No, but it's often overused. Composition is usually more flexible. Use inheritance when there's a clear hierarchical "is-a" relationship.
Summary
Inheritance lets child classes receive properties and methods from parent classes, creating hierarchies where specialized classes build upon general ones.
Key Takeaways:
- Child inherits everything from parent
- Use
extendsto create inheritance super()calls parent constructor (required!)super.method()calls parent's version- Children can override parent methods
- Use when there's a clear "IS-A" relationship
- Keep hierarchies shallow (2-3 levels max)
- When in doubt, prefer composition
Inheritance is a powerful tool, but like any tool, use it where it fits. Good code often mixes inheritance with composition.
Related Concepts
Leave a Comment
Comments (0)
Be the first to comment on this concept.
Comments are approved automatically.