Prototypal Inheritance
JavaScript's prototype-based object model where objects delegate behavior to other objects through an internal [[Prototype]] chain, forming the basis of property lookup and code reuse.
Definition
Prototypal inheritance is a mechanism where an object can delegate property and method access to another object (its prototype). Every JavaScript object has an internal [[Prototype]] link (accessible via Object.getPrototypeOf() or the deprecated __proto__). When accessing a property that doesn't exist on the object itself, the engine walks up the prototype chain until it finds the property or reaches null. This differs from class-based inheritance in that there are no class blueprints — objects inherit directly from other objects.
Context and scope
Prototypal inheritance underlies JavaScript's object system and explains ES6 class syntax (which is syntactic sugar over prototypes), object spread behavior, the difference between own and inherited properties, and why 'this' binding in methods depends on call site, not definition site. All built-in types (Array, Date, RegExp) have prototypes with shared methods that instances delegate to.
Common misconceptions
- ES6 classes are fundamentally different from prototypes — classes still use prototypes under the hood; class methods live on Class.prototype
- Modifying an object's prototype affects only new objects — the [[Prototype]] link is live, so adding methods to a prototype is immediately visible to all objects delegating to it
- 'this' refers to the object that defines the method — 'this' is determined by how the function is called, which in prototype methods is the receiver before the dot
Anti-patterns
- Mutating built-in prototypes (Array.prototype.customMethod) in library code — causes name collisions and breaks other libraries
- Creating deep prototype chains unnecessarily — walks slow and complicates debugging
- Using Object.setPrototypeOf() on existing objects — deoptimizes V8 hidden class transitions; prefer Object.create() for new objects
Related concepts
Related examples
Authoritative source
https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
What Is Prototypal Inheritance
Prototypal inheritance means objects can delegate behavior to other objects. Instead of copying methods from a class blueprint, JavaScript objects forward property lookups along a live link to a prototype object.
The Prototype Chain
Every object has a [[Prototype]] internal slot. When you access obj.prop:
- Check if
propexists directly onobj(own property). - If not, check
obj.[[Prototype]]forprop. - Continue up the chain until found or chain ends at
null.
const animal = { eats: true };
const rabbit = Object.create(animal);
rabbit.jumps = true;
console.log(rabbit.jumps); // true (own property)
console.log(rabbit.eats); // true (delegated to animal)
How Classes Use Prototypes
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} makes a noise`; }
}
class Dog extends Animal {
speak() { return `${this.name} barks`; }
}
// Equivalent prototype-based code:
function Animal2(name) { this.name = name; }
Animal2.prototype.speak = function() { return this.name + ' makes a noise'; };
function Dog2(name) { Animal2.call(this, name); }
Dog2.prototype = Object.create(Animal2.prototype);
Dog2.prototype.constructor = Dog2;
Dog2.prototype.speak = function() { return this.name + ' barks'; };
Own vs. Inherited Properties
const obj = { a: 1 };
const child = Object.create(obj);
child.b = 2;
console.log(Object.keys(child)); // ['b'] — own enumerable only
console.log('a' in child); // true — checks entire chain
console.log(child.hasOwnProperty('a')); // false
Deduplication via Shared Prototypes
Methods live once on the prototype, shared by all instances:
function User(name) { this.name = name; } // constructor runs per instance
User.prototype.greet = function() { // shared across all instances
return `Hi, I'm ${this.name}`;
};
const a = new User('Alice');
const b = new User('Bob');
console.log(a.greet === b.greet); // true — same function object
Modern Alternative: Object.create()
For direct delegation without constructors:
const logger = {
log(msg) { console.log(`[LOG]: ${msg}`); },
};
const service = Object.create(logger);
service.run = function() { this.log('Running...'); };
service.run(); // "[LOG]: Running..."
Performance Note
Walking a deep prototype chain on every property access can be slow. V8 optimizes with hidden classes, but frequent Object.setPrototypeOf() calls defeat those optimizations. Use flat structures and create objects with stable prototypes.
When to Use Prototype Patterns
- Shared methods across many instances (DOM-like hierarchies)
- Behavior delegation (object composition alternative to mixins)
- Polyfilling or extending built-in APIs (caution advised)
Related Concepts
- Lexical Scope: Determines
thisat function definition - Object Composition: Prefer delegating state, inheriting behavior
- Proxy: Intercepts all property access on an object