Explain the prototype chain and prototypal inheritance
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.
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
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.