Back to JavaScript
JavaScript
medium
mid

Can you explain the prototype chain and prototypal inheritance in JavaScript?

Every object has an internal `[[Prototype]]` link to another object. Property lookup walks that chain. `Object.create`, `class`, and constructor functions all set up the same chain.

6 min read·~12 min to think through

JavaScript inheritance is delegation: when you read a property, the engine looks on the object, then on its prototype, then its prototype, and so on until null.

Two ideas to keep separate:

  • Object.prototype — the property name on a constructor function that holds the methods to be shared with instances.
  • __proto__ / [[Prototype]] — the link an instance has to its prototype.

new Foo() does roughly: create {}, link its [[Prototype]] to Foo.prototype, run Foo with this = the new object, return the object. That's why methods on Foo.prototype are shared and methods inside the constructor are per-instance (and waste memory).

class Foo {} is sugar over the same machinery. extends Bar sets Foo.prototype.[[Prototype]] = Bar.prototype so instances inherit through two links.

Why it matters for memory: a thousand instances with prototype methods share one method object. A thousand instances with arrow class fields each carry their own copy.

Code

ts
class Animal { speak() { return "..."; } }
class Dog extends Animal { bark() { return "woof"; } }

const d = new Dog();
d.bark();                                // Dog.prototype.bark
d.speak();                               // walks: d → Dog.prototype → Animal.prototype
Object.getPrototypeOf(d) === Dog.prototype;       // true
Object.getPrototypeOf(Dog.prototype) === Animal.prototype; // true
Prototype chain walk
ts
const base = { greet() { return `hi ${this.name}`; } };
const ada = Object.create(base);
ada.name = "Ada";
ada.greet(); // "hi Ada"
Object.create — direct prototype linkage

Follow-up questions

  • Why are prototype methods cheaper than methods defined in the constructor?
  • How does `Object.hasOwn` differ from `in`?
  • What does `super` do in a class method, mechanically?

Common mistakes

  • Mutating `Array.prototype` and breaking the global namespace.
  • Confusing `__proto__` with `prototype` — the former is the link on instances, the latter is on constructors.
  • Using arrow functions as class methods and then expecting prototype-shared behavior.

Performance considerations

  • Long prototype chains slightly slow lookup; modern engines inline-cache hot paths.
  • Adding properties dynamically to instances (vs declaring them in the class) blows V8's hidden-class optimization.

Edge cases

  • `Object.create(null)` makes an object with no prototype — useful for maps that shouldn't accidentally have `.toString`.
  • Built-in subclasses (`extends Array`) work but corner cases (\`Symbol.species\`) get tricky.

Real-world examples

  • Express middleware composition uses a prototype chain (Application → router → app) for method delegation.

Senior engineer discussion

Senior signal: discuss V8 hidden classes, transition trees, how prototype mutation invalidates inline caches, and why \`Object.setPrototypeOf\` is slow.

Related questions