Classes
A class in JavaScript compiles down to the exact same constructor-function-plus-prototype mechanism from the Prototypes topic. It does not introduce a new object model — it introduces cleaner syntax for one that already existed.
🎯 What you'll learn
- Classes Are Syntactic Sugar Over Prototypes
- Anatomy of a Class
#privateFields Are Real Privacy- Inheritance with
extendsandsuper - Class Methods Are Not Hoisted Like Function Declarations
1Classes Are Syntactic Sugar Over Prototypes
A class in JavaScript compiles down to the exact same constructor-function-plus-prototype mechanism from the Prototypes topic. It does not introduce a new object model — it introduces cleaner syntax for one that already existed.
class Dog {
constructor(name) {
this.name = name;
}
bark() {
return `${this.name} says woof`;
}
}
// Equivalent, pre-class syntax:
function DogFn(name) { this.name = name; }
DogFn.prototype.bark = function () { return `${this.name} says woof`; };bark lives on Dog.prototype, shared by every instance — identical to the manual prototype version.
2Anatomy of a Class
class BankAccount {
static bankName = "JS National Bank"; // shared across ALL instances, not per-instance
#balance; // private field — only accessible inside this class
constructor(owner, initialBalance = 0) {
this.owner = owner;
this.#balance = initialBalance;
}
deposit(amount) { // instance method — on the prototype
this.#balance += amount;
return this.#balance;
}
get balance() { // getter — accessed like a property, not called
return this.#balance;
}
static create(owner) { // static method — called on the class, not instances
return new BankAccount(owner);
}
}| Feature | Syntax | Lives on | Shared or per-instance |
|---|---|---|---|
| Instance field | field = value; | instance | per-instance |
| Private field | #field; | instance | per-instance, truly inaccessible outside |
| Instance method | method() {} | prototype | shared |
| Static field/method | static x = ... | the class itself | shared, one copy total |
| Getter/setter | get x() {} / set x(v) {} | prototype | shared, accessed as a property |
3#private Fields Are Real Privacy
Unlike closures-based privacy, #field access outside the class is a syntax error, not just convention:
class Secret {
#value = 42;
reveal() { return this.#value; }
}
const s = new Secret();
console.log(s.reveal()); // 42
console.log(s.#value); // SyntaxError — # fields aren't just "hidden", they're inaccessible syntax4Inheritance with extends and super
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // MUST call super() before using `this` in a derived constructor
this.breed = breed;
}
speak() { // overrides Animal's speak
return `${super.speak()}, specifically a bark`; // super.method() calls the parent's version
}
}
const rex = new Dog("Rex", "Labrador");
console.log(rex.speak()); // "Rex makes a sound, specifically a bark"
console.log(rex instanceof Animal); // trueextends links Dog.prototype's prototype to Animal.prototype — extending the prototype chain by one link, exactly as in the Prototype Chain topic.
5Class Methods Are Not Hoisted Like Function Declarations
const early = new Foo(); // ReferenceError — classes are NOT hoisted the way function declarations are
class Foo {}Classes are hoisted (their name is reserved) but remain in the Temporal Dead Zone until the declaration line runs — same rule as let/const.
⚠️ Common Pitfalls
- Forgetting
super()in a derived class constructor — accessingthisbefore callingsuper()throws aReferenceError. In a derived class,thisdoesn't exist untilsuper()runs. - Class methods are not auto-bound — detaching a method (
const fn = instance.method) losesthis, exactly like regular prototype methods (see thethiskeyword topic). Use arrow class fields if you need auto-binding. - Static members are not inherited to instances —
BankAccount.bankNameworks;accountInstance.bankNamedoes not, unless accessed viathis.constructor.bankName. - Class bodies are always strict mode —
thisin a detached method call isundefined, never the global object, regardless of the surrounding file's mode. - Forgetting classes aren't hoisted like functions — you cannot instantiate a class before its declaration line, even though the name is reserved earlier.
📋 Quick Reference
class X extends Y {
static s = ... // one per class
#private = ... // one per instance, truly private
constructor() { super(); } // must call super() first if extending
method() {} // shared via X.prototype
get/set prop() {} // accessed as a property
}🎤 Interview Q&A
Q1What features do JavaScript classes have?core▾
Constructors, methods on the prototype, static members, getters/setters, inheritance with extends/super, public and private fields (#count), static blocks, and private methods.
- Are #private fields truly private? Yes, enforced by the language, unlike
_convention.
Q2Why must you call super() before using this in a subclass constructor?core▾
The parent constructor creates/initializes the object; until super() runs, this isn't initialized and accessing it throws.
- When can you omit the constructor? If it only calls super with the same args.
Q3Composition vs inheritance in JavaScript?senior▾
Prefer composition (objects containing or delegating to others, functions, mixins) for flexibility; use inheritance for clear is-a hierarchies with shared behaviour. Deep hierarchies become brittle.
- Mixin pattern? Functions that take a class and return an extended class.
Q4What's the difference between static and instance members?core▾
Static members belong to the class itself (Math.max, factory methods, caches); instance members belong to each object.
- Can static methods access instance fields? Not without an instance.
Q5How do getters and setters help?core▾
They expose computed or validated properties with property syntax (circle.area), letting you add logic without changing the API.
- Pitfall? Expensive getters look like cheap property reads.
🛠️ Practice exercises
Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.