Topic 12 / 15

Classes, Prototypes & this

~10 min read  //  JavaScript Series  //  Coding India

Class Syntax

class Course {
  #students = 0;                    // private field — invisible outside

  constructor(title, price) {
    this.title = title;
    this.price = price;
  }

  enroll() {
    this.#students++;
  }

  get revenue() {                   // getter — read like a property
    return this.#students * this.price;
  }

  static fromJSON(json) {           // called on the class, not instances
    const data = JSON.parse(json);
    return new Course(data.title, data.price);
  }
}

const js = new Course("JS Mastery", 199);
js.enroll();
js.revenue;                 // 199 — no parentheses
js.#students;               // SyntaxError — truly private
Course.fromJSON('{"title":"CSS","price":99}');

Inheritance

class VideoCourse extends Course {
  constructor(title, price, hours) {
    super(title, price);            // must call before using this
    this.hours = hours;
  }

  get revenue() {
    return super.revenue * 1.1;     // call the parent's version
  }
}

Under the Hood: Prototypes

class is syntax over JavaScript’s real model: prototype chains. Methods live once on Course.prototype; instances link to it. When you call js.enroll(), JS looks on the instance, then up the chain until it finds the method. That’s also why "abc".toUpperCase() works — strings link to String.prototype. You rarely touch prototypes directly, but knowing the model demystifies the language.

this — the Part Everyone Trips On

this is determined by how a function is called, not where it’s written:

const course = {
  title: "JS",
  describe() {
    return this.title;       // called as course.describe() → this = course
  },
};

course.describe();           // 'JS'

const fn = course.describe;
fn();                        // undefined — called bare, this is lost!

The classic bug — passing a method as a callback:

button.addEventListener("click", course.describe);        // this is lost

// Fix 1: arrow wrapper (most common)
button.addEventListener("click", () => course.describe());

// Fix 2: bind
button.addEventListener("click", course.describe.bind(course));

Arrow functions don’t have their own this — they inherit it from where they’re defined. That’s why class field arrows are handy for callbacks:

class Counter {
  count = 0;
  increment = () => {          // arrow field: this is always the instance
    this.count++;
  };
}

When to Use Classes

JS is multi-paradigm. Use classes for stateful things with identity (a player, a connection, a game). For data transformation, plain functions + objects are often simpler — don’t force OOP where a function will do.