JS::OOP_Mastery
Overview / Session 02
Session 02 of 10  ·  2 hrs + 15 min review

The class Keyword & Instantiation

⏱ 2-hour session 📋 15-min review 🟡 Builds on Session 01 🗂 4 topics

1 — From Object Literals to Blueprints

Session 01 showed you object literals — one object, written by hand. That's fine for a single cart or a single config. But what happens when you need fifty users, each with the same shape and the same methods? Copy-pasting the literal fifty times is not a plan.

A class is a blueprint for objects. You define the shape and behaviour once; then you stamp out as many independent objects from it as you need. Think of the class as a cookie cutter and each object as a cookie — same shape, but each one is separate and can hold its own state (sprinkles, icing — or in code terms, its own property values).

JavaScript — the problem with copy-paste literals
// Works, but doesn't scale — every user repeats the same shape and logic
const user1 = { name: 'Alice', role: 'admin', describe() { return `${this.name} (${this.role})`; } };
const user2 = { name: 'Bob',   role: 'editor', describe() { return `${this.name} (${this.role})`; } };
// ...and again, and again, for every user you create

2 — Defining Your First Class

A class declaration uses the class keyword, PascalCase by convention (User, not user), and a special method called constructor() that runs every time you create a new instance.

JavaScript — Anatomy of a class
/**
 * Class declaration: PascalCase by convention
 */
class User {
    constructor(name, role) {
        this.name = name;   // instance property
        this.role = role;   // instance property
    }

    describe() {                  // instance method
        return `${this.name} (${this.role})`;
    }
}

const user1 = new User('Alice', 'admin');
const user2 = new User('Bob', 'editor');

console.log(user1.describe()); // "Alice (admin)"
console.log(user2.describe()); // "Bob (editor)"

Same blueprint, two independent objects. Changing user1.name has zero effect on user2 — each instance owns its own copy of the instance properties set inside the constructor.

What new actually does

When you write new User(...), four things happen in order:

  • A brand-new, empty object is created
  • That object's internal prototype is linked to User.prototype (more on this in Session 03)
  • The constructor() runs, with this bound to the new object
  • The new object is returned automatically — you don't write return yourself

3 — Class Fields: Properties Without a Constructor Line

Modern JavaScript (ES2022+) lets you declare instance properties directly in the class body, skipping the this.x = x boilerplate when a property has a fixed default or doesn't depend on a constructor argument.

JavaScript — class fields
class ShoppingCart {
    items = [];           // class field — fresh array per instance
    discountRate = 0.1;  // class field with a default value

    constructor(owner) {
        this.owner = owner;  // still fine to set some props in the constructor
    }

    addItem(name, price) {
        this.items.push({ name, price });
    }

    total() {
        const sum = this.items.reduce((a, i) => a + i.price, 0);
        return sum * (1 - this.discountRate);
    }
}

const cart = new ShoppingCart('Alice');
cart.addItem('Keyboard', 80);
console.log(cart.total()); // 72

The critical detail: items = [] as a class field runs per instance. Each new ShoppingCart gets its own fresh empty array — they don't share one array between every cart. That's exactly the encapsulation win from Session 01, now formalized by the class.

4 — Instance Methods vs Object-Literal Methods

A method written inside a class body looks similar to the shorthand method syntax from Session 01 — but there's a meaningful difference in where it lives.

JavaScript — where methods actually live
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    distanceFromOrigin() {
        return Math.sqrt(this.x ** 2 + this.y ** 2);
    }
}

const p1 = new Point(3, 4);
const p2 = new Point(6, 8);

// p1 and p2 each have their own x/y...
console.log(p1.x === p2.x);              // false — different own properties

// ...but they SHARE the exact same distanceFromOrigin function
console.log(p1.distanceFromOrigin === p2.distanceFromOrigin); // true

Methods defined in a class body are placed once on Point.prototype and shared by every instance — they are not recreated per object the way class fields are. Object-literal methods, by contrast, belong to that one specific object. This is a memory-efficiency detail today; in Session 03 it becomes the key to understanding what class is actually doing behind the scenes.

15-Minute Review — Session 02

Stop the session timer and switch to the review timer in the sidebar. Answer these questions from memory — then check.

Q1 — What is the main reason to use a class instead of writing object literals by hand?

Q2 — Which special method runs automatically every time you create a new instance with new?

Q3 — You declare items = [] as a class field. What happens when you create two instances of that class?

Q4 — Two instances of the same class, p1 and p2. What is true about their instance methods (e.g. p1.describe vs p2.describe)?

✓ Key concepts checklist

Next: Prototypes — What class Really Is →