Prototypal Inheritance
Introduction
JavaScript inheritance is prototype delegation. Objects inherit from other objects by having those objects in their prototype chain. Constructor functions and class syntax are structured ways to create and link those objects.
The most important interview sentence: ES6 class is syntactic sugar over prototypes, not a switch to classical Java-style inheritance.
Why This Matters
Senior JavaScript interviews often ask you to translate between constructor functions, Object.create, and class extends. Understanding the common prototype mechanics lets you debug instanceof, fix broken constructor links, avoid shared mutable prototype state, and choose composition when inheritance would make the design brittle.
Theory
Prototype-based, not class-based at the core
JavaScript objects delegate to other objects. class syntax gives a familiar declaration style, but methods still live on .prototype, instances still have internal [[Prototype]] links, and extends still creates a prototype chain between prototype objects.
Constructor-function inheritance pattern
Before ES6 classes, a common pattern was:
- Call the parent constructor inside the child constructor with
Parent.call(this, ...)to initialise instance data. - Set
Child.prototype = Object.create(Parent.prototype)so child instances can inherit parent methods. - Restore
Child.prototype.constructor = Childbecause replacing the prototype object overwrites the default constructor reference. - Add child-specific methods to
Child.prototype.
ES6 class syntax
class Child extends Parent performs the prototype linking for you. super(...) calls the parent constructor. Methods declared in the class body are placed on the prototype, not copied onto every instance.
instanceof mental model
value instanceof Constructor checks whether Constructor.prototype appears anywhere in value's prototype chain. It is chain-based, not field-based.
Prefer composition when possible
Inheritance is useful for true substitutability and shared protocol. Composition is often better for UI and application logic because behaviours can be assembled without deep fragile chains.
Visual Diagrams
dog instance
+----------------------+
| own name: 'Rex' |
+----------------------+
|
v
Dog.prototype
+----------------------+
| speak: function |
| constructor: Dog |
+----------------------+
|
v
Animal.prototype
+----------------------+
| eat: function |
| constructor: Animal |
+----------------------+
|
v
Object.prototype
+----------------------+
| toString, valueOf |
+----------------------+
|
v
null`extends` and the older `Object.create` pattern both build chains like this.
Code Examples
Pre-ES6 constructor-function inheritance
This is the pattern that class extends makes easier to read, while still relying on prototypes underneath.
The same relationship with class syntax
Class syntax is clearer, but methods still live on prototypes and extends still creates prototype-chain links.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1function Parent() {}2 3Parent.prototype.say = function () {4 return 'parent';5};6 7function Child() {}8 9Child.prototype = Object.create(Parent.prototype);10Child.prototype.constructor = Child;11 12const child = new Child();13 14console.log(child.say());15console.log(child instanceof Parent);16console.log(child.constructor === Child);Predict the output #2
1function User(name) {2 this.name = name;3}4 5User.prototype.tags = [];6 7const ada = new User('Ada');8const grace = new User('Grace');9 10ada.tags.push('admin');11console.log(grace.tags.join(','));12 13grace.tags = ['editor'];14console.log(ada.tags.join(','));15console.log(grace.tags.join(','));Coding Exercises
Implement constructor inheritance
HardCreate Vehicle and Car constructor functions. Vehicle should store make and expose describe() on its prototype. Car should inherit from Vehicle, store model, restore its constructor, and override describe() to include both make and model.
Interview Questions
1Is JavaScript `class` real class-based inheritance?
class is syntax over JavaScript's prototype system. Class methods are stored on the constructor's .prototype, instances delegate through [[Prototype]], and extends links prototype objects. It feels class-like, but the runtime inheritance mechanism is still prototypal delegation.
Follow-ups
- Where do class methods live?
- What does `super` do in a derived constructor?
2How does `instanceof` work?
obj instanceof Fn checks whether Fn.prototype appears anywhere in obj's prototype chain. It does not check which constructor function originally ran or whether the object has particular fields. Changing prototypes can therefore affect instanceof results.
Quiz
1. In constructor-function inheritance, why set `Child.prototype = Object.create(Parent.prototype)`?
2. Where are methods declared inside an ES6 class body usually stored?
Summary
- JavaScript inheritance is prototype delegation at runtime.
- Constructor-function inheritance links child prototypes with `Object.create(Parent.prototype)`.
- ES6 `class` and `extends` are clearer syntax over the same prototype mechanics.
- Avoid shared mutable prototype state; create instance-specific arrays and objects per instance.
Cheat Sheet
Old pattern: Parent.call(this, args) + Child.prototype = Object.create(Parent.prototype) + restore constructor.
Class pattern: class Child extends Parent { constructor(...) { super(...); } }
Core truth: class syntax still uses prototypes.
instanceof: checks whether Constructor.prototype appears in the object's chain.
Pitfall: arrays or objects on prototypes are shared across instances.