ES6 Classes

Course 2 · Ch 3
ES6 Classes: Constructors, Methods, and Inheritance with extends
A cleaner syntax for the same object-with-methods pattern from Fundamentals Chapter 7 — plus a real way to share behaviour between related types

Fundamentals Chapter 7 built objects by hand, one at a time, each with its own copy of methods like greet() defined inline. class syntax (added in the same 2015 update as let/const) provides a blueprint for creating many similarly-shaped objects, plus a built-in way to extend one type's behaviour from another — JavaScript's equivalent of inheritance.

Defining a Class

class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hi, I'm ${this.name}`); } } const p = new Person("Philip", 35); p.greet(); // "Hi, I'm Philip"

constructor(...) runs automatically whenever new Person(...) is called — its job is to set up the new object's starting properties via this. Every other method (greet here) is defined directly inside the class body, with no function keyword and no commas between methods — a more compact form than Fundamentals Chapter 7's object-literal method syntax, but functionally similar underneath.

A class is really just a special kind of function
Behind the scenes, class is mostly a cleaner syntax over JavaScript's existing object/prototype system — typeof Person actually returns "function". The new keywords (class, constructor, extends) exist purely for readability; they don't introduce a fundamentally different kind of object.

Multiple Instances, Each With Their Own Data

const alice = new Person("Alice", 28); const bob = new Person("Bob", 42); alice.greet(); // "Hi, I'm Alice" bob.greet(); // "Hi, I'm Bob"

Each new Person(...) call produces a completely independent object with its own name/age — but all instances share the exact same greet method definition, stored once rather than duplicated per object, which is more memory-efficient than the object-literal approach from Chapter 7 at scale.

Inheritance with extends

class Student extends Person { constructor(name, age, school) { super(name, age); // calls Person's constructor first this.school = school; } study() { console.log(`${this.name} is studying at ${this.school}`); } } const student = new Student("Sam", 20, "MIT"); student.greet(); // "Hi, I'm Sam" — inherited from Person student.study(); // "Sam is studying at MIT" — Student's own method

class Student extends Person makes Student a more specific version of Person — it automatically gets every method Person has (greet), plus whatever new methods it adds itself (study). super(name, age) must be called before this can be used at all inside a subclass constructor — it runs the parent class's constructor logic first, so this.name/this.age get set up correctly before Student adds its own school property.

Forgetting super() in a subclass constructor is a hard error
If a subclass defines its own constructor, it MUST call super(...) before referencing this anywhere — JavaScript throws a ReferenceError immediately otherwise. If a subclass needs no extra setup at all, its constructor can simply be omitted entirely; the parent's constructor runs automatically in that case.

Overriding a Method

class Student extends Person { constructor(name, age, school) { super(name, age); this.school = school; } greet() { super.greet(); // still runs Person's original greet() first console.log(`I study at ${this.school}`); } } new Student("Sam", 20, "MIT").greet(); // "Hi, I'm Sam" // "I study at MIT"

Defining greet again inside Student replaces (overrides) the inherited version for any Student instance. super.greet() inside the override still reaches back to Person's original method — useful when the subclass wants to extend, not completely replace, the parent's behaviour.

Getters — Computed Properties That Look Like Plain Fields

class Rectangle { constructor(width, height) { this.width = width; this.height = height; } get area() { return this.width * this.height; } } const rect = new Rectangle(4, 5); console.log(rect.area); // 20 — read like a property, not called like a method

get area() defines a method that's accessed WITHOUT parentheses — rect.area, not rect.area() — recalculated fresh every time it's read. Useful for values that are always derivable from existing properties (width × height) rather than stored separately, avoiding the risk of them drifting out of sync.

ConceptFundamentals Ch 7 (object literal)This chapter (class)
Creating an instance{ name: ..., greet() {...} }new Person(name)
Method sharingDuplicated per objectShared once across all instances
Extending behaviourNo built-in mechanismclass Sub extends Base
Computed propertyA method called with ()get propertyName() — read without ()

Coding Challenges

Challenge 1

Define a class Animal with a constructor taking name, and a method speak() logging "{name} makes a sound." Create two Animal instances with different names and call speak() on each.

📄 View solution
Challenge 2

Define a class Dog that extends Animal from Challenge 1, overriding speak() to log "{name} barks." instead, while still calling the parent's speak() first using super.speak(). Create a Dog instance and call speak() on it.

📄 View solution
Challenge 3

Define a class Circle with a constructor taking radius, and a getter area returning the circle's area (π × radius², using 3.14159 for π) and a getter circumference returning 2 × π × radius. Create an instance and log both computed properties.

📄 View solution

Chapter 3 Quick Reference

  • class Name { constructor(...) {...} method() {...} } — basic class shape
  • new ClassName(...) — creates an instance, running the constructor
  • class Sub extends Base — inherits all of Base's methods automatically
  • super(...) — calls the parent constructor; required before using this in a subclass constructor
  • super.method() — calls the parent's version of an overridden method
  • get propertyName() {...} — a method read without parentheses, like a plain property
  • class is mostly syntax over JavaScript's existing object/prototype system
  • Next chapter: this in depth — call/apply/bind, and arrow vs regular function context