ES6 Classes
Syntactic Sugar Over Prototypes
LinkedIn Hook
Most JavaScript developers think
classbrought "real" classes to the language.It didn't.
Every
classyou write is compiled down to exactly the same prototype pattern we've been using since 1995 — a constructor function, methods hung on.prototype, andObject.createlinking parent and child chains.
class Dog extends Animalis literallyDog.prototype = Object.create(Animal.prototype)plus some safety rules.But classes DO add genuinely new things: non-enumerable methods, forced
strictmode, no hoisting, must-use-new,super, and finally — truly private fields with#.In this lesson you'll see class syntax and prototype syntax side-by-side, learn what
super()actually compiles to, and understand the 4 real differences between classes and plain constructor functions.After this, when someone asks "are JavaScript classes real classes?" you'll give the answer that impresses senior engineers.
Read the full lesson -> [link]
#JavaScript #InterviewPrep #ES6 #Classes #Prototypes #Frontend #CodingInterview #OOP
What You'll Learn
- How
class,constructor,extends, andsupermap directly to prototype patterns - Where static methods live and why they don't exist on instances
- How
#private fields, non-enumerable methods, and strict mode differentiate classes from plain functions
Classes Are Functions in Disguise
ES6 classes look like classical OOP — but under the hood, they're just functions and prototypes. The class keyword is syntactic sugar over JavaScript's existing prototype-based inheritance.
Let's prove it.
Class Syntax vs Prototype Syntax — Side by Side
// ============= ES6 CLASS =============
class PersonClass {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return "Hi, I'm " + this.name;
}
static create(name, age) {
return new PersonClass(name, age);
}
}
// ============= EQUIVALENT PROTOTYPE =============
function PersonFunc(name, age) {
this.name = name;
this.age = age;
}
PersonFunc.prototype.greet = function () {
return "Hi, I'm " + this.name;
};
// static method = property on the constructor itself
PersonFunc.create = function (name, age) {
return new PersonFunc(name, age);
};
// They work identically:
const p1 = new PersonClass("Rakibul", 25);
const p2 = new PersonFunc("Rakibul", 25);
console.log(p1.greet()); // "Hi, I'm Rakibul"
console.log(p2.greet()); // "Hi, I'm Rakibul"
// Proof that class is just a function:
console.log(typeof PersonClass); // "function"
console.log(PersonClass.prototype.constructor === PersonClass); // true
Inheritance — extends & super
// ============= ES6 CLASS =============
class Animal {
constructor(name) {
this.name = name;
}
eat() {
return this.name + " is eating";
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // calls Animal's constructor
this.breed = breed;
}
bark() {
return this.name + " says Woof!";
}
}
// ============= EQUIVALENT PROTOTYPE =============
function AnimalFunc(name) {
this.name = name;
}
AnimalFunc.prototype.eat = function () {
return this.name + " is eating";
};
function DogFunc(name, breed) {
AnimalFunc.call(this, name); // super(name) equivalent
this.breed = breed;
}
DogFunc.prototype = Object.create(AnimalFunc.prototype);
DogFunc.prototype.constructor = DogFunc;
DogFunc.prototype.bark = function () {
return this.name + " says Woof!";
};
// Both produce identical prototype chains:
const d1 = new Dog("Rex", "Shepherd");
const d2 = new DogFunc("Rex", "Shepherd");
console.log(d1.bark()); // "Rex says Woof!"
console.log(d1.eat()); // "Rex is eating"
console.log(d1 instanceof Dog); // true
console.log(d1 instanceof Animal); // true
Static Methods
class MathUtils {
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
}
// Static methods live on the constructor, NOT the prototype
console.log(MathUtils.add(2, 3)); // 5
console.log(MathUtils.prototype.add); // undefined
// You cannot call static methods on instances
const m = new MathUtils();
// m.add(2, 3); // TypeError: m.add is not a function
// Under the hood, it's just:
// MathUtils.add = function(a, b) { return a + b; };
Private Fields (#)
class BankAccount {
#balance; // private field — cannot be accessed outside the class
#pin;
constructor(owner, initialBalance, pin) {
this.owner = owner;
this.#balance = initialBalance;
this.#pin = pin;
}
#validatePin(pin) { // private method
return pin === this.#pin;
}
deposit(amount) {
this.#balance += amount;
return this.#balance;
}
withdraw(amount, pin) {
if (!this.#validatePin(pin)) {
throw new Error("Invalid PIN");
}
if (amount > this.#balance) {
throw new Error("Insufficient funds");
}
this.#balance -= amount;
return this.#balance;
}
getBalance(pin) {
if (!this.#validatePin(pin)) {
throw new Error("Invalid PIN");
}
return this.#balance;
}
}
const account = new BankAccount("Rakibul", 1000, 1234);
console.log(account.owner); // "Rakibul" (public)
console.log(account.deposit(500)); // 1500
console.log(account.getBalance(1234)); // 1500
// These all fail:
// console.log(account.#balance); // SyntaxError: Private field
// console.log(account.#pin); // SyntaxError: Private field
// account.#validatePin(1234); // SyntaxError: Private field
Key Differences Between Classes and Plain Functions
class Example {
constructor() {}
}
// 1. Classes are NOT hoisted (unlike function declarations)
// const e = new MyClass(); // ReferenceError
// class MyClass {}
// 2. Classes always run in strict mode
class StrictCheck {
method() {
// 'this' is undefined if method is called without context
// (not window, because strict mode)
return this;
}
}
const fn = new StrictCheck().method;
console.log(fn()); // undefined (strict mode, not window)
// 3. Classes cannot be called without 'new'
// Example(); // TypeError: Class constructor Example cannot be invoked without 'new'
// 4. Class methods are non-enumerable
console.log(Object.keys(Example.prototype)); // [] (methods don't show up)
// With prototype syntax, they ARE enumerable:
function Func() {}
Func.prototype.method = function () {};
console.log(Object.keys(Func.prototype)); // ["method"]
Common Mistakes
- Thinking
classcreates real classes — it creates a function.typeof MyClass === "function", and methods live onMyClass.prototype. - Forgetting to call
super()before usingthisin a subclass constructor — the spec requiressuper()first, otherwise you get aReferenceError. - Calling a class without
new(e.g.Example()instead ofnew Example()) — unlike plain constructor functions, classes throwTypeErrorwhen invoked withoutnew.
Interview Questions
Q: Are JavaScript classes real classes?
No. JavaScript classes are syntactic sugar over prototype-based inheritance.
classcreates a constructor function, methods go on the prototype,extendssets up the prototype chain, andsupercalls the parent constructor. Under the hood, it's all prototypes.
Q: What does super() do in a constructor?
super()calls the parent class's constructor. It's equivalent toParentConstructor.call(this, args). In a derived class, you MUST callsuper()before usingthis, or you'll get a ReferenceError.
Q: What are private fields in JavaScript?
Private fields (prefixed with
#) are class properties that cannot be accessed or modified outside the class. They're enforced at the language level — not by convention (like the_prefixpattern). Accessing them outside the class throws a SyntaxError.
Q: Can you name 3 differences between class and function constructors?
- Classes are not hoisted — you can't use them before declaration. 2) Classes always run in strict mode. 3) Class methods are non-enumerable. 4) Classes must be called with
new— calling without it throws TypeError.
Q: What is a static method and where does it live?
A
staticmethod is attached to the constructor itself, not to.prototype. It's called asClassName.method()and is not accessible on instances (instance.method()is undefined). Under the hood,static method(){}is equivalent toClassName.method = function(){}.
Quick Reference — Cheat Sheet
ES6 CLASSES = SYNTACTIC SUGAR
class -> function (typeof === "function")
constructor -> function body
methods -> on Class.prototype (non-enumerable)
static method -> property on the constructor itself
extends Parent -> Object.create(Parent.prototype) chain
super(args) -> Parent.call(this, args)
#privateField -> true private (language-enforced, SyntaxError on leak)
Class-only rules:
- NOT hoisted (TDZ until declaration)
- ALWAYS runs in strict mode
- MUST be called with `new` (else TypeError)
- Methods are NON-enumerable (Object.keys returns [])
- super() must be called before `this` in derived constructor
Previous: Object.create() Next: instanceof & typeof
This is Lesson 5.4 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.