Day 7: Prototypes, Prototypal Inheritance & Class Transpilation
See JS classes for what they really are — sugar over prototype chains — so inheritance bugs and `instanceof` checks stop being mysterious.
Study
Concepts
Every object has a hidden link: [[Prototype]]
Objects in JS inherit through a chain of internal links (`[[Prototype]]`, exposed as `Object.getPrototypeOf(obj)` or the legacy `__proto__`). Reading a property that is not found on the object itself makes the engine walk this chain upward, checking each prototype in turn, until it finds the property or reaches `null`.
Functions have a special `.prototype` property (only on the function object, not on instances) that becomes the `[[Prototype]]` of every object created with `new Fn()`. That is the entire mechanism behind constructor-based inheritance.
class is syntax, not a new inheritance model
A `class` declaration desugars to: a constructor function, methods attached to `ClassName.prototype` (non-enumerable), and `extends` wiring up both the prototype chain (`Sub.prototype.__proto__ = Super.prototype`) and a static chain (`Sub.__proto__ = Super`) so static methods inherit too. `super()` inside a subclass constructor calls the parent constructor with the correct `this`.
The main real differences from old-style prototype code: class bodies run in strict mode, methods are non-enumerable by default, and a class cannot be called without `new` (throws a TypeError) — none of that is possible to fully replicate with plain functions, but the *lookup mechanism* is identical.
See It
Visualizations
Visualization
Prototype chain for an Employee instance
Property lookup walks up until found or hits null.
Build It
Code Examples
Pre-ES6: constructor functions + prototype (what class desugars to)
function Person(name) {
this.name = name;
}
Person.prototype.introduce = function () {
return `I am ${this.name}`;
};
function Employee(name, role) {
Person.call(this, name); // "super()" equivalent
this.role = role;
}
Employee.prototype = Object.create(Person.prototype); // wire the chain
Employee.prototype.constructor = Employee;
Employee.prototype.work = function () {
return `${this.introduce()}, working as ${this.role}`;
};
const emp = new Employee('Ali', 'Engineer');
console.log(emp.work()); // "I am Ali, working as Engineer"
console.log(emp instanceof Person); // true — walks the chainThe equivalent modern class
class Person {
constructor(name) {
this.name = name;
}
introduce() {
return `I am ${this.name}`;
}
}
class Employee extends Person {
constructor(name, role) {
super(name); // calls Person's constructor with the right 'this'
this.role = role;
}
work() {
return `${this.introduce()}, working as ${this.role}`;
}
}
const emp = new Employee('Ali', 'Engineer');
console.log(Object.getPrototypeOf(emp) === Employee.prototype); // true
console.log(Object.getPrototypeOf(Employee.prototype) === Person.prototype); // trueRemember
Key Takeaways
- Property lookup walks [[Prototype]] links upward until found or null — this IS "inheritance" in JS.
- A function's .prototype becomes new instances' [[Prototype]] when called with new.
- class is sugar: methods land on ClassName.prototype, extends wires the prototype AND static chains.
- super() in a subclass constructor is literally ParentConstructor.call(this, ...args) under the hood.
- instanceof checks whether a prototype appears anywhere in the chain — not the object's "type".
Do It
Practice
- 1Build a 3-level prototype chain (Animal → Dog → Puppy) without using class, only functions and Object.create.
- 2Add a method to Object.prototype and prove every object in your program suddenly has it — then explain why this is dangerous in real code.
- 3Use Object.getPrototypeOf repeatedly on a class instance to manually print its full chain up to null.