Phase 2 · Core JavaScriptModule 14~48 min read

Prototypes & Classes

Learn JavaScript's prototype model and the class syntax built on top of it.

What you'll learn

JavaScript objects delegate property lookup through prototype chains. Class syntax offers a familiar, disciplined way to construct objects while preserving that delegation model underneath.

By the end of this lesson, you'll be able to:

  • Trace a prototype lookup
  • Define classes with instance and static behavior
  • Choose composition or inheritance deliberately

Core mental model

Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.

ConceptWhat it meansDecision rule
Prototype chainMissing properties are searched on linked prototypesShare methods instead of copying them per object
ClassSyntax for constructors and prototype methodsUse when a domain has stable instance identity and invariants
CompositionObjects collaborate through contained capabilitiesPrefer it when behavior varies independently

Professional workflow

Build the behavior in small, observable steps. Each step should leave something you can inspect or test.

  1. Describe the object behavior model boundary: inputs, outputs, state, timing, and expected failures.
  2. Implement the smallest correct path with names that expose intent.
  3. Add edge cases and failure handling before introducing abstractions.
  4. Verify behavior with realistic data and one deliberately adversarial example.
  5. Refactor only after the observable behavior is protected.

Make behavior observable

Before optimizing or abstracting, make inputs, outputs, state changes, timing, and failure paths visible. JavaScript becomes much easier to reason about when hidden work is exposed.

Guided code lab

Build an invariant-rich class

Private state can only change through validated public methods.

bank-account.js
class BankAccount {
  #balance = 0;
  constructor(owner) { this.owner = owner; }
  deposit(amount) {
    if (amount <= 0) throw new RangeError("Amount must be positive");
    this.#balance += amount;
  }
  get balance() { return this.#balance; }
}
const account = new BankAccount("Lina");
account.deposit(50);
console.log(account.owner, account.balance);

Delegate through a prototype

Object.create links the new object to shared behavior.

prototype.js
const speaker = { speak() { return this.name + " speaks"; } };
const learner = Object.create(speaker);
learner.name = "Omar";
console.log(learner.speak());
console.log(Object.getPrototypeOf(learner) === speaker);

Production practice

Contract

Constructors establish valid instances; public methods preserve their invariants.

Verification

Test own properties, inherited methods, invalid construction, and subclass boundaries.

Operations

Keep inheritance trees shallow and profile only when object volume justifies it.

Common failure mode

Using inheritance only to reuse code creates rigid hierarchies; composition often models changing capabilities better.

Independent workshop

Model subscriptions with a base account and composable billing and notification capabilities.

Your finished workshop must include:

  • A class with private state
  • At least one shared prototype method
  • A written composition-vs-inheritance decision

Definition of done

Demonstrate the happy path and at least two edge cases, keep responsibilities separated, and add a short note explaining one design choice.

Recap & quick check

Key takeaways

  • Objects delegate through prototypes
  • Classes use prototypes underneath
  • Private fields protect invariants
  • Composition reduces hierarchy coupling

Quick check

1. Where are class methods normally stored?

2. What starts a private field name?

3. What happens after an own-property lookup misses?

Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.