IntermediateJavaScript · Lesson 4 of 10

Classes, Inheritance & Prototypes

class syntax, constructors, getters, static members, private #fields, extends and the prototype chain.

Classes are templates for objects that share behaviour. The constructor sets up each new instance; methods are shared by all instances. Getters (get average()) look like properties but compute their value.

#private fields can't be read or changed from outside the class — real encapsulation. static members belong to the class itself, such as factory methods (Student.fromCSV).

extends creates a subclass; call super(...) in its constructor. Under the hood, JavaScript classes use prototypes: each object links to a prototype object that holds the shared methods, and property lookups walk up this chain.

classes.jsJavaScript
class Student {
  #scores = [];                            // private field
  static passMark = 30;

  constructor(name, form) {
    this.name = name;
    this.form = form;
  }

  addScore(score) {
    if (score < 0 || score > 100) throw new RangeError("score must be 0-100");
    this.#scores.push(score);
    return this;                           // allows chaining
  }

  get average() {
    if (this.#scores.length === 0) return 0;
    return this.#scores.reduce((a, b) => a + b, 0) / this.#scores.length;
  }

  get passed() {
    return this.average >= Student.passMark;
  }

  static fromCSV(line) {
    const [name, form] = line.split(",");
    return new Student(name.trim(), Number(form));
  }

  toString() {
    return `${this.name} (Form ${this.form}): ${this.average.toFixed(1)}`;
  }
}

class Prefect extends Student {
  constructor(name, form, duty) {
    super(name, form);
    this.duty = duty;
  }

  toString() {
    return `${super.toString()} - prefect for ${this.duty}`;
  }
}

const amina = new Student("Amina", 4).addScore(88).addScore(79);
const neema = new Prefect("Neema", 4, "library").addScore(71);
const juma = Student.fromCSV("Juma, 3").addScore(25);

for (const s of [amina, neema, juma]) console.log(String(s), s.passed ? "PASS" : "FAIL");

console.log(neema instanceof Student);                                 // true
console.log(Object.getPrototypeOf(neema) === Prefect.prototype);       // true
console.log(Object.getPrototypeOf(Prefect.prototype) === Student.prototype); // true
console.log("scores" in amina, Object.keys(amina));                    // false [ 'name', 'form' ]
Runs in your browser · JavaScript

Key points

  • Classes bundle data and behaviour; methods live on the shared prototype.
  • #private fields give real encapsulation; getters compute values on access.
  • Prefer shallow inheritance — composition is often simpler than deep class trees.

Exercise

Create a Shape base class with an area getter and describe() method, and Circle and Rectangle subclasses. Store mixed shapes in an array, sort them by area and print each description.

Show solution

Try the exercise yourself first — then compare your approach with this one.

Shape defines the shared describe() method and expects subclasses to provide an area getter. toSorted returns a new array ordered by area, leaving the original untouched.

shapes.jsJavaScript
class Shape {
  get area() {
    throw new Error("Subclasses must implement area");
  }

  describe() {
    return `${this.constructor.name} with area ${this.area.toFixed(2)}`;
  }
}

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

  get area() {
    return Math.PI * this.radius ** 2;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  get area() {
    return this.width * this.height;
  }
}

const shapes = [new Rectangle(3, 4), new Circle(1), new Rectangle(2, 2), new Circle(2)];
for (const s of shapes.toSorted((a, b) => a.area - b.area)) console.log(s.describe());
// Circle with area 3.14 / Rectangle with area 4.00 / Rectangle with area 12.00 / Circle with area 12.57
Runs in your browser · JavaScript

Check your understanding

  1. What must a subclass constructor call before using this?

  2. What makes a field written as #scores special?

  3. Where do a class's methods live?

  4. What is a static method such as Student.fromCSV(line)?

Ask AI