Suggest an editImprove this articleRefine the answer for “Prototypes and prototypal inheritance in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Prototypal inheritance** lets objects delegate property lookups to other objects through a chain of `[[Prototype]]` links. ```javascript const animal = { eat() { console.log('eating'); } }; const dog = Object.create(animal); // dog's [[Prototype]] = animal dog.bark = () => console.log('woof'); dog.eat(); // delegates up to animal dog.bark(); // own method ``` **Key point:** nothing is copied. JS walks the chain at runtime until it finds the property or hits `null`.Shown above the full answer for quick recall.Answer (EN)Image**Prototypal inheritance** in JavaScript means objects delegate property lookups to other objects through a chain of live links, rather than copying behavior at creation time. ## Theory ### TL;DR - Analogy: you ask your dad for his pasta recipe. If he doesn't remember, he asks his dad. The chain ends at `null`. - Main difference from classical OOP: delegation at runtime, not copying at creation. - V8 stores `[[Prototype]]` as a hidden pointer and walks the chain on every property miss. - `Object.create()` for explicit delegation, classes for readable hierarchies (they use the same mechanism under the hood). - Chains deeper than 10 levels slow lookups noticeably in V8. Keep it shallow. ### Quick example ```javascript const parent = { greeting: 'Hi' }; const child = Object.create(parent); // child's [[Prototype]] = parent console.log(child.greeting); // 'Hi' — delegated up the chain child.surname = 'Smith'; console.log(child.surname); // 'Smith' — own property console.log(parent.surname); // undefined — parent was not mutated ``` `child` has no `greeting` of its own, so JS walks up to `parent` and finds it there. Setting `child.surname` creates an own property on `child`, not on `parent`. The parent stays untouched. ### Key difference from classical inheritance Classical languages like Java copy parent behavior into each child at creation. The child is self-contained from that point forward. JavaScript does the opposite: nothing is copied. The child holds a live reference to its parent object, and the engine walks that reference at runtime on every lookup. Changes to the parent propagate to all children instantly, unless a child has shadowed that property with its own value. ### When to use - Sharing methods across many instances (like `Array.prototype.map`) — prototypes. - Fixed hierarchies with clear structure and private state — classes (they use prototypes internally anyway). - Dynamic extension at runtime for plugins or mixins — `Object.create()`. - Performance-critical paths — avoid chains deeper than 5 levels as a safe limit. V8 documents degraded performance at 10+ levels. ### How V8 handles it V8 stores `[[Prototype]]` as a hidden pointer on every object. When you access a property, V8 runs `GetProperty`, checks own keys first via inline caches (`LoadIC`), and on a miss follows the pointer to the next object. This repeats until `null`, which returns `undefined`. Two things hurt performance here. First, calling `Object.setPrototypeOf()` on an existing object invalidates V8's inline caches, forcing a re-optimization pass. Do it once at creation, not in loops. Second, chains deeper than 10 objects push inline caches into a megamorphic state, making lookups roughly 5-10x slower per the V8 blog. ### Common mistakes **Mutating a shared prototype and expecting instance isolation.** ```javascript const proto = { count: 0 }; const a = Object.create(proto); const b = Object.create(proto); proto.count++; // both a and b see this change console.log(a.count, b.count); // 1 1 ``` The prototype is shared by reference, not copied. If each instance needs its own value, set it directly on the instance: `a.count = 0`. **`for...in` iterates the entire chain.** ```javascript const obj = Object.create({ inherited: 'surprise' }); obj.own = 'mine'; for (let key in obj) console.log(key); // 'own', then 'inherited' ``` Use `Object.keys()` for own properties only, or add `Object.prototype.hasOwnProperty.call(obj, key)` inside the loop. **Calling `Object.setPrototypeOf()` in a loop.** ```javascript let obj = {}; for (let i = 0; i < 100; i++) { Object.setPrototypeOf(obj, {}); // invalidates V8 caches on every iteration } ``` Build the prototype chain once with `Object.create()`. Mutating it repeatedly is a V8 deoptimization trap. **Assuming `instanceof` checks only one level.** ```javascript const plain = Object.create(null); // no Object.prototype at all console.log(plain instanceof Object); // false ``` `Object.create(null)` produces a pure dictionary with no chain. Useful for hashmaps without prototype pollution risk, but `instanceof`, `toString()`, and similar methods will not work on it. ### Real-world usage - React: synthetic events delegate to a shared prototype for pool reuse (ReactDOMSyntheticEvent). - Node.js: `http.Server` inherits from `net.Server` via the prototype chain, which is how EventEmitter methods reach HTTP handlers. - Express: middleware delegation for shared validators and auth checks. - Vue.js: component instances share a common options prototype. - Every array delegates `.map()`, `.filter()`, `.reduce()` from `Array.prototype`. ### Follow-up questions **Q:** What is the output? ```javascript const a = {}; const b = Object.create(a); a.x = 1; delete a.x; console.log(b.x); ``` **A:** `undefined`. After `delete a.x` the property is gone from `a`. `b` delegates to `a`, finds nothing, returns `undefined`. **Q:** What is the difference between `__proto__` and `[[Prototype]]`? **A:** `[[Prototype]]` is the internal slot defined in the spec. `__proto__` is a getter/setter on `Object.prototype` that exposes it. It is non-standard in strict environments and deprecated in production code. Use `Object.getPrototypeOf()` instead. **Q:** How do ES6 classes relate to prototypes? **A:** Classes are syntax sugar over the same mechanism. `class Person { greet() {} }` creates a constructor function and writes `greet` to `Person.prototype`. `Object.getPrototypeOf(new Person()) === Person.prototype` is always true. **Q:** What is `Object.create(null)` useful for? **A:** It creates an object with no prototype chain at all. No inherited `toString`, no `hasOwnProperty`, no prototype keys. That makes it a clean dictionary, safe for storing arbitrary user-supplied keys without prototype pollution risk. **Q:** (Senior) V8 optimizes shallow chains. At what depth does it deoptimize, and what is the practical cost? **A:** Around 10-15 levels triggers megamorphic inline cache states. The V8 blog documents roughly 5x slower lookups on a 20-level chain compared to a 2-level one. If you are building a plugin system with dynamic `Object.create()` chains, this is worth benchmarking with your actual data. ## Examples ### Basic prototype chain delegation ```javascript const vehicle = { type: 'vehicle', describe() { console.log(`This is a ${this.type}`); } }; const car = Object.create(vehicle); car.type = 'car'; // shadows vehicle.type const sportsCar = Object.create(car); // sportsCar has no type of its own, delegates to car sportsCar.describe(); // 'This is a car' console.log(Object.getPrototypeOf(sportsCar) === car); // true console.log(Object.getPrototypeOf(car) === vehicle); // true ``` `sportsCar` has no `describe`, so the engine walks up to `car`. `car` has none either, so it continues to `vehicle` where it finds the method. Inside that call, `this.type` resolves to `car.type` because `sportsCar` delegates to `car`, which has that property set as its own. ### Constructor functions and shared methods This is how shared methods worked before ES6 classes. It is also the exact mechanism classes use internally. ```javascript function User(name, role) { this.name = name; this.role = role; } User.prototype.canEdit = function() { return this.role === 'admin'; }; User.prototype.greet = function() { console.log(`Hi, I am ${this.name}`); }; const alice = new User('Alice', 'admin'); const bob = new User('Bob', 'viewer'); alice.greet(); // 'Hi, I am Alice' console.log(alice.canEdit()); // true console.log(bob.canEdit()); // false // Both share the same function references console.log(alice.canEdit === bob.canEdit); // true ``` `new User(...)` creates a plain object and sets its `[[Prototype]]` to `User.prototype`. The functions live once on that object, not duplicated per instance. That is the memory-efficiency argument for prototypes: 1000 instances share one copy of each method. ### Prototype mutation surprise in production I have seen this bug in real codebases when developers mix shared prototype state with per-instance logic. ```javascript const SyntheticEventProto = { bubbles: false, preventDefault() { this.defaultPrevented = true; } }; const clickEvent = Object.create(SyntheticEventProto); clickEvent.type = 'click'; clickEvent.preventDefault(); console.log(clickEvent.defaultPrevented); // true const mouseEvent = Object.create(SyntheticEventProto); // Someone mutates the shared prototype directly SyntheticEventProto.bubbles = true; console.log(clickEvent.bubbles); // true — shared surprise console.log(mouseEvent.bubbles); // true — same prototype, same change ``` `clickEvent.defaultPrevented` is safe because `preventDefault()` sets `this.defaultPrevented`, which creates an own property on the instance. But mutating `SyntheticEventProto.bubbles` directly changes the shared object. Every instance that has not shadowed `bubbles` with its own value now sees `true`. React's actual implementation (ReactDOMSyntheticEvent) uses similar prototype pooling, which is why reading event properties asynchronously after React 16 could return unexpected values.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.