# JavaScript Inheritance and super: Extend Classes and Call Parents

Source: https://devdocs10.com/articles/javascript/js-inheritance  
By Murat Bo · Published 2026-07-11 · DevDocs10 (https://devdocs10.com)

JavaScript inheritance lets one class reuse and specialize the behavior of another class. The `super` keyword is how a subclass calls the parent constructor or parent methods, which makes class hierarchies easier to build and maintain.

> **Quick answer:** Use `extends` to make one class inherit from another, then use `super()` in the subclass constructor before touching `this`. Use `super.methodName()` to reuse parent method logic inside an override.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic class syntax, constructors, methods, and how objects store state in JavaScript.

## 1. What Is JavaScript Inheritance and super?

Inheritance is a way to build a new class from an existing class. The new class, called a subclass, gets the parent class’s methods and can add new behavior or replace existing behavior. The `super` keyword connects the two classes.

- `extends` creates an inheritance relationship between two classes.
- `super()` calls the parent class constructor.
- `super.methodName()` calls a parent method from an overridden child method.
- Inheritance is based on JavaScript’s prototype system, even though the syntax looks class-based.

In practice, inheritance is useful when one type is a more specific version of another type, such as `Car` and `ElectricCar`, or `Employee` and `Manager`.

## 2. Why JavaScript Inheritance Matters

Inheritance helps you avoid repeating shared behavior across related objects. Instead of copying the same methods into several classes, you define them once in a parent class and reuse them in child classes.

It matters because it can make code easier to organize when multiple types share a common base. It also lets you add specialized behavior without losing the shared behavior.

You should not use inheritance just to share any code. If two classes are not truly related, composition or helper functions are often clearer than a deep inheritance tree.

## 3. Basic Syntax

### Extending a parent class

The `extends` keyword tells JavaScript that one class inherits from another. The child class then gets the parent class’s prototype methods automatically.

```javascript
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return `${this.name} makes a sound.`;
  }
}

class Dog extends Animal {
  bark() {
    return `${this.name} barks.`;
  }
}
```

This example creates a parent class and a subclass. `Dog` inherits `speak()` from `Animal` and adds its own `bark()` method.

### Calling the parent constructor with super()

If the parent class needs initialization, the child constructor must call `super()`. That runs the parent setup before the child adds its own state.

```javascript
class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}
```

`super(name)` passes the `name` argument to the parent constructor. Only after that can the child safely use `this`.

## 4. Step-by-Step Examples

### Example 1: Inheriting a parent method

Here, the child class adds a new method but still uses the parent’s shared behavior.

```javascript
class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    return `${this.name} is eating.`;
  }
}

class Cat extends Animal {
  meow() {
    return `${this.name} says meow.`;
  }
}

const cat = new Cat("Milo");
console.log(cat.eat());
console.log(cat.meow());
```

Output (tested with Node 24.21.0):

```text
Milo is eating.
Milo says meow.
```

The `Cat` instance can call both inherited and own methods. This is the simplest form of inheritance.

### Example 2: Overriding a method and using super.methodName()

A child class can replace a parent method while still reusing part of the parent’s logic.

```javascript
class Vehicle {
  describe() {
    return "This is a vehicle.";
  }
}

class Car extends Vehicle {
  describe() {
    return `${super.describe()} It has four wheels.`;
  }
}

const car = new Car();
console.log(car.describe());
```

Output (tested with Node 24.21.0):

```text
This is a vehicle. It has four wheels.
```

`super.describe()` runs the parent version of `describe()`. This is useful when the subclass needs to extend, not completely replace, the parent behavior.

### Example 3: Adding child-specific state after super()

Subclass constructors often accept extra data. The parent handles the shared data, and the child handles the new fields.

```javascript
class Employee {
  constructor(name) {
    this.name = name;
  }
}

class Manager extends Employee {
  constructor(name, teamSize) {
    super(name);
    this.teamSize = teamSize;
  }
}

const manager = new Manager("Ava", 8);
```

The parent class initializes the shared `name` property, and the child adds `teamSize`. This pattern keeps shared and specific state separate.

### Example 4: Using inherited methods through instances

Inherited methods are available on the child instance as if they were defined there, but they actually come from the parent prototype chain.

```javascript
class Account {
  constructor(balance) {
    this.balance = balance;
  }

  deposit(amount) {
    this.balance += amount;
    return this.balance;
  }
}

class SavingsAccount extends Account {
  addInterest(rate) {
    this.balance *= 1 + rate;
    return this.balance;
  }
}

const account = new SavingsAccount(100);
account.deposit(25);
account.addInterest(0.1);
```

This shows that inherited and child-specific methods work together on the same object. The child class does not lose the parent’s functionality.

## 5. Practical Use Cases

- Modeling a base entity and specialized variants, such as `Shape` with `Circle` and `Rectangle`.
- Building shared UI or domain behavior, such as a base `FormField` class with specialized input types.
- Creating common infrastructure objects, such as a base `Repository` with specialized database access methods.
- Reusing validation or formatting logic across closely related objects.
- Overriding a parent method to refine behavior while keeping the original implementation available through `super`.

Inheritance works best when the child class really is a kind of parent class. If the relationship feels forced, prefer composition, where one object contains another object and delegates work to it.

## 6. Common Mistakes

### Mistake 1: Using this before super() in a subclass constructor

In a derived class, JavaScript requires `super()` to run before you access `this`. This rule exists because the parent constructor must initialize the instance first.

**Problem:** This causes a runtime error such as `ReferenceError: Must call super constructor in derived class before accessing 'this'`.

```javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name) {
    this.name = name;
    super(name);
  }
}
```

**Fix:** Call `super()` first, then use `this`.

```javascript
class Dog extends Animal {
  constructor(name) {
    super(name);
    this.breed = "unknown";
  }
}
```

The corrected version works because the parent constructor is responsible for initializing the subclass instance before any child-specific fields are assigned.

### Mistake 2: Forgetting to call super() in a derived constructor

If a subclass defines its own constructor, it must call `super()` unless it extends a class with an empty constructor and does not use `this`. Most real subclasses need the parent initialization.

**Problem:** JavaScript throws an error because the derived constructor never initializes the instance through the parent class.

```javascript
class Employee {
  constructor(name) {
    this.name = name;
  }
}

class Manager extends Employee {
  constructor(name) {
    this.role = "manager";
  }
}
```

**Fix:** Pass the needed arguments to `super()` before assigning child properties.

```javascript
class Manager extends Employee {
  constructor(name) {
    super(name);
    this.role = "manager";
  }
}
```

This version works because the parent class can finish its own setup before the child adds extra state.

### Mistake 3: Overriding a method and accidentally losing parent behavior

Sometimes a child method should add behavior instead of replacing it. If you override without calling the parent version, you may drop important logic.

**Problem:** The child method works, but it silently skips the parent method’s behavior, which can break formatting, logging, validation, or state updates.

```javascript
class Report {
  format() {
    return "Base report format.";
  }
}

class SalesReport extends Report {
  format() {
    return "Sales report only.";
  }
}
```

**Fix:** Call the parent method with `super` when you want to extend its result.

```javascript
class SalesReport extends Report {
  format() {
    return `${super.format()} Sales data included.`;
  }
}
```

The corrected version preserves the base behavior and adds the child-specific detail on top.

## 7. Best Practices

### Practice 1: Use inheritance for true is-a relationships

Inheritance is strongest when the subclass can safely stand in for the parent class. If that relationship is not natural, your code will become fragile over time.

```javascript
class Shape {
  area() {
    throw new Error("Implement in subclass");
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }
}
```

This keeps the hierarchy meaningful. The parent class defines a shared contract, and each child provides its own implementation.

### Practice 2: Keep parent constructors small and focused

A parent constructor should initialize shared state, not do too much work. Large constructors make subclasses harder to extend and test.

```javascript
class User {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
}
```

When the constructor stays focused, child classes can reliably call `super()` and add their own state without surprising side effects.

### Practice 3: Override only what needs changing

If a child class only needs a small change, reuse the parent method and adjust the result instead of rewriting everything.

```javascript
class Greeter {
  message() {
    return "Hello";
  }
}

class FriendlyGreeter extends Greeter {
  message() {
    return `${super.message()} there!`;
  }
}
```

This approach keeps the subclass small and makes the intent obvious.

## 8. Limitations and Edge Cases

- Inheritance is single in JavaScript classes: a class can extend only one parent class.
- The `super` keyword works differently in constructors and methods. In constructors it calls the parent constructor; in methods it calls a parent method.
- Static methods can also use `super` to call inherited static behavior, but the lookup follows the class itself rather than an instance.
- Subclassing built-in objects like `Array` or `Error` works in modern JavaScript, but behavior can be more subtle than with plain classes.
- Inheritance does not copy methods into the child class. They are resolved through the prototype chain, so changing the parent prototype affects all subclasses that use it.
- Deep inheritance trees can be harder to understand than small, focused classes with composition.

> A common surprise is that inherited methods may appear to be “owned” by the child instance, but they are still found through prototypes behind the scenes.

## 9. Practical Mini Project

Let’s build a small notification system with a shared base class and two specialized subclasses. The parent class will handle common notification data, while the child classes customize how messages are formatted.

```javascript
class Notification {
  constructor(recipient, message) {
    this.recipient = recipient;
    this.message = message;
  }

  summary() {
    return `To: ${this.recipient} | Message: ${this.message}`;
  }
}

class EmailNotification extends Notification {
  constructor(recipient, message, subject) {
    super(recipient, message);
    this.subject = subject;
  }

  summary() {
    return `${super.summary()} | Subject: ${this.subject}`;
  }
}

class SmsNotification extends Notification {
  summary() {
    return `SMS -> ${super.summary()}`;
  }
}

const email = new EmailNotification("alex@example.com", "Your order has shipped", "Order Update");
const sms = new SmsNotification("Sam", "Your package is out for delivery");

console.log(email.summary());
console.log(sms.summary());
```

Output (tested with Node 24.21.0):

```text
To: alex@example.com | Message: Your order has shipped | Subject: Order Update
SMS -> To: Sam | Message: Your package is out for delivery
```

This mini project shows the full inheritance flow: a shared base class, child constructors, and method overrides that reuse parent logic through `super`.

## 10. Key Points

- `extends` creates inheritance between two classes.
- `super()` must run before using `this` in a derived constructor.
- `super.methodName()` lets a child method reuse parent logic.
- Inheritance is best for true parent-child relationships, not just code reuse.
- JavaScript inheritance uses the prototype chain under class syntax.

## 11. Practice Exercise

- Create a base class named `Task` with `title` and `completed` properties.
- Create a subclass named `TimedTask` that adds a `dueDate` property.
- Add a `summary()` method to `Task`.
- Override `summary()` in `TimedTask` and reuse the parent result with `super.summary()`.

**Expected output:** The subclass should print the task title, completion state, and due date.

**Hint:** Put the shared fields in the parent constructor, call `super()` from the child constructor, and extend the parent method result instead of replacing it entirely.

```javascript
class Task {
  constructor(title, completed) {
    this.title = title;
    this.completed = completed;
  }

  summary() {
    return `Task: ${this.title} | Completed: ${this.completed}`;
  }
}

class TimedTask extends Task {
  constructor(title, completed, dueDate) {
    super(title, completed);
    this.dueDate = dueDate;
  }

  summary() {
    return `${super.summary()} | Due: ${this.dueDate}`;
  }
}

const task = new TimedTask("Write docs", false, "2026-08-01");
console.log(task.summary());
```

Output (tested with Node 24.21.0):

```text
Task: Write docs | Completed: false | Due: 2026-08-01
```

## 12. Final Summary

JavaScript inheritance lets one class reuse and specialize another class through the `extends` keyword. This is a good fit when your objects have a clear parent-child relationship and share meaningful behavior.

The `super` keyword is the bridge between the subclass and the parent class. Use `super()` in a derived constructor to initialize the parent part of the object, and use `super.methodName()` when an overridden method needs to reuse parent logic.

When used carefully, inheritance can reduce duplication and make your class design easier to follow. The key is to keep the hierarchy shallow, the responsibilities clear, and the parent-child relationship genuinely logical.
