Prototypes & Classes
Prototype chain, class syntax, inheritance, composition
What you'll learn
Understand prototype chain
Compare constructor functions vs class syntax
Use extends, super, static, private fields
Prefer composition over inheritance
Prototype Chain
Objects have [[Prototype]]. Property lookup walks the chain.
const animal = { eats: true }
const rabbit = { jumps: true }
Object.setPrototypeOf(rabbit, animal)
rabbit.jumps // true (own), rabbit.eats // true (prototype), rabbit.toString // Object.prototype
Constructor vs Class
// Constructor function (pre-ES6)
function User(name, role) {
this.name = name
this.role = role
}
User.prototype.greet = function () {
return "Hi, I'm " + this.name
}
new User("Alice", "admin").greet() // "Hi, I'm Alice"
// Class syntax (ES6 sugar)
class User {
constructor(name, role) {
this.name = name
this.role = role
}
greet() {
return "Hi, I'm " + this.name
}
}
extends & super
class Animal {
constructor(name) {
this.name = name
}
speak() {
return this.name + " makes a sound"
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name)
this.breed = breed
}
speak() {
return this.name + " barks"
}
}
new Dog("Rex", "Husky") instanceof Animal // true
Static & Private
class MathUtils {
static clamp(v, min, max) {
return Math.min(Math.max(v, min), max)
}
}
MathUtils.clamp(15, 0, 10) // 10
class Account {
#balance = 0
constructor(v) {
this.#balance = v
}
getBalance() {
return this.#balance
}
}
// acct.#balance // SyntaxError — truly private
Composition Over Inheritance
Assemble behavior from small pieces.
const canFly = (obj) => ({ ...obj, fly: () => obj.name + " flies" })
const canSwim = (obj) => ({ ...obj, swim: () => obj.name + " swims" })
const duck = canSwim(canFly({ name: "Daffy", speak: () => "quacks" }))
duck.fly() // "Daffy flies"
See It In Action
UI Element Hierarchy
Code
javascript
1class UIElement {2constructor(id) { this.id = id }3render() { return '<div id="' + this.id + '"></div>' }4validate() { return null }5}6 7class Button extends UIElement {8constructor(id, label, onClick) {9super(id)10this.label = label11this.onClick = onClick12}13render() {14return '<button id="' + this.id + '">' + this.label + '</button>'15}16validate() {17if (!this.label) throw new Error("Label required")18return null19}20click() { if (this.onClick) this.onClick(this.label) }21}22 23class TextField extends UIElement {24constructor(id, placeholder) {25super(id)26this.placeholder = placeholder27this.value = ""28}29render() {30return '<input id="' + this.id + '" placeholder="' + this.placeholder + '" />'31}32getValue() { return this.value }33}34 35const btn = new Button("btn1", "Submit", console.log)36console.log(btn.render()) // <button id="btn1">Submit</button>`Button`and`TextField`extend`UIElement`via`extends`+`super()`, inheriting the base render contract while adding their own properties and methods. This is the class-based inheritance pattern in JavaScript.
Lesson Summary
Key Takeaways
Prototype chain: objects inherit from other objects
Class syntax is sugar over constructor + prototype
extends + super for hierarchies; static for class-level; # for private fields
Prefer composition — mix behavior instead of deep inheritance trees