# Inheritance with extends and super

A class that extends another starts with everything the parent has, calls super to let the parent set up its part, and can add methods or override the parent's with its own version.

Journeyman · JavaScript · OOP · Updated November 1, 2026

## After this lesson you can

- Write a class that extends another and calls super in its constructor
- Override a method and call the parent's version with super

## Key takeaways

- class Fighter extends Ship makes Fighter a child of Ship. Every fighter gets Ship's properties and methods without writing them again.
- A child's constructor must call super(...) before it uses this. super runs the parent's constructor.
- A child can add its own methods, such as fire(), that only its instances have.
- A child overrides a method by writing one with the same name. Inside it, super.describe() still runs the parent's version.
- A loop can call describe() on fighters, freighters and plain ships alike, and each answers in its own way.

Red Squadron needs more than one kind of ship. A fighter is a ship with missiles. A freighter is a ship with a cargo hold. Both still have a name, shields and fuel, and both still take hits. Copying the whole `Ship` class twice would mean fixing every bug three times. Real programs hit this all the time: a store's products where books, shirts and gift cards share most things, or a game's enemies, each with its own trick. **Inheritance** lets one class build on another, and it's how the libraries you'll use later (and this course's *Virtual Pet* project, with its `Dragon` that extends `Pet`) are put together.

Every example in this lesson starts with this `Ship` class. Make a file named `hangar.js` and put the class at the top. To run an example, paste it under the class, in place of the example before it:

```javascript
class Ship {
  constructor(name, shields, fuel) {
    this.name = name;
    this.shields = shields;
    this.fuel = fuel;
  }

  describe() {
    return `${this.name}: shields ${this.shields}, fuel ${this.fuel}`;
  }
}
```

## A ship, and then some

Think of a basic cheese pizza and the specials built on it. A pepperoni pizza *is a* cheese pizza with pepperoni on top: same dough, same sauce, same oven, plus one thing. Nobody writes the dough recipe again for it.

In JavaScript, **`extends`** says "this class is that class, plus some". The class it builds on is the **parent class**, and the new one is the **child class**. A child with nothing in it at all already has everything the parent has:

```javascript
class Freighter extends Ship {}

const muleOne = new Freighter("Mule One", 150, 80);
console.log(muleOne.describe());
```

```text
Mule One: shields 150, fuel 80
```

`Freighter` has no constructor and no `describe` of its own, yet Mule One gets both from `Ship`. A good test for inheritance is the words "is a": a freighter *is a* ship, so `extends` fits.

## super: let the parent set up its part

A freighter also carries cargo, so its constructor needs a fourth value. The child's constructor calls **`super(...)`** first: `super` runs the parent's constructor, which stores the name, shields and fuel. Then the child stores what's new:

```javascript
class Freighter extends Ship {
  constructor(name, shields, fuel, cargo) {
    super(name, shields, fuel);
    this.cargo = cargo;
  }
}

const muleOne = new Freighter("Mule One", 150, 80, 40);
console.log(muleOne.cargo);
console.log(muleOne.describe());
```

```text
40
Mule One: shields 150, fuel 80
```

**`super(...)` must come before any use of `this`** in a child's constructor. Swap the two lines, and the program stops when it builds Mule One:

```text
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
```

The error's *derived class* is another name for a child class. Until the parent has built its part of the object, there's no `this` to store the cargo on.

## Adding methods of its own

A child can have methods the parent doesn't. Only fighters fire missiles:

```javascript
class Fighter extends Ship {
  constructor(name, shields, fuel, missiles) {
    super(name, shields, fuel);
    this.missiles = missiles;
  }

  fire() {
    if (this.missiles === 0) {
      return `${this.name} is out of missiles!`;
    }
    this.missiles = this.missiles - 1;
    return `${this.name} fires! ${this.missiles} left`;
  }
}

const comet = new Fighter("Comet", 100, 50, 2);
console.log(comet.fire());
console.log(comet.fire());
console.log(comet.fire());
```

```text
Comet fires! 1 left
Comet fires! 0 left
Comet is out of missiles!
```

`fire` reads `this.name`, which `Ship`'s constructor stored, and `this.missiles`, which `Fighter`'s stored: it's all one object. A plain `Ship` has no `fire`: inheritance goes from parent to child, never the other way.

**Try it.** In `hangar.js`, give `Freighter` a method `unload(crates)` that takes crates out of the cargo and returns a line such as *Mule One unloads 15 crates, 25 left*. Build Mule One with 40 crates and unload twice.

## Overriding: your own version of a parent's method

Comet's `describe()` still says nothing about missiles, because it's `Ship`'s version. A child **overrides** a method by writing one with the same name: JavaScript uses the child's version for the child's instances.

To reuse the parent's line instead of writing it out again, call **`super.describe()`**: inside a child's method, `super.` reaches the parent's version:

```javascript
class Fighter extends Ship {
  constructor(name, shields, fuel, missiles) {
    super(name, shields, fuel);
    this.missiles = missiles;
  }

  describe() {
    return `${super.describe()}, missiles ${this.missiles}`;
  }
}

const comet = new Fighter("Comet", 100, 50, 4);
console.log(comet.describe());
```

```text
Comet: shields 100, fuel 50, missiles 4
```

If `Ship`'s line ever changes (say it adds a ship's speed), every fighter's line follows, because it's built from `super.describe()`.

One thing a child can't reach: its parent's private fields. If `Ship` kept `#shields` private, `Fighter`'s methods would read them through `Ship`'s getter, `this.shields`, like any other outside code.

## One loop, many kinds of ship

Here's where it pays off. A fleet holds fighters, freighters and plain ships in one array, and a loop asks each to describe itself:

```javascript
class Fighter extends Ship {
  constructor(name, shields, fuel, missiles) {
    super(name, shields, fuel);
    this.missiles = missiles;
  }

  describe() {
    return `${super.describe()}, missiles ${this.missiles}`;
  }
}

class Freighter extends Ship {
  constructor(name, shields, fuel, cargo) {
    super(name, shields, fuel);
    this.cargo = cargo;
  }

  describe() {
    return `${super.describe()}, cargo ${this.cargo} crates`;
  }
}

const fleet = [
  new Fighter("Comet", 100, 50, 4),
  new Freighter("Mule One", 150, 80, 40),
  new Ship("Scout", 60, 30),
];

for (const ship of fleet) {
  console.log(ship.describe());
}
```

```text
Comet: shields 100, fuel 50, missiles 4
Mule One: shields 150, fuel 80, cargo 40 crates
Scout: shields 60, fuel 30
```

The loop doesn't check what kind of ship it has. It calls `describe()`, and each ship answers in its own way. **In your textbook this is called polymorphism** ("many shapes"); parent and child classes are also called *base* and *derived* classes, or *superclass* and *subclass*.

![A family tree of ships: Ship at the top with a name, shields, fuel and describe; Fighter and Freighter below it, each adding one thing of its own, missiles or cargo](https://fsdtrainingmedia.blob.core.windows.net/public/images/2026/10/2815606aef624cf8b3a7f98e3c70e134.webp)

## Check yourself

2 questions to try on the lesson page.

## So what

`extends` and `super` let you write what's shared once and only what's different in each child. When a game adds a new kind of enemy, or a store a new kind of product, it's one small class, and every loop that already works with the parent works with it too.

## Key terms

- [Inheritance](https://www.ask-codey.com/glossary/inheritance-javascript)
- [extends](https://www.ask-codey.com/glossary/extends)
- [super](https://www.ask-codey.com/glossary/super-javascript)
- [Method Overriding](https://www.ask-codey.com/glossary/method-overriding-javascript)
- [Parent Class](https://www.ask-codey.com/glossary/parent-class)
- [Child Class](https://www.ask-codey.com/glossary/child-class)

## Cheat sheets

- [Inheritance with extends and super](https://www.ask-codey.com/cheat-sheets/javascript-classes-extends-and-super-cheat-sheet)

## Related

- [Private Fields, Getters and Setters](https://www.ask-codey.com/lessons/javascript-classes-private-fields-getters-setters)
- [Prototypes: What Classes Are Made Of](https://www.ask-codey.com/lessons/javascript-classes-prototypes)

Source: https://www.ask-codey.com/lessons/javascript-classes-extends-and-super
