How JavaScript's Prototype Chain Works for Inheritance: Delegation vs. Copying
JavaScript implements inheritance through an internal hidden linkage called [[Prototype]] that delegates property lookups up a chain of objects until finding the property or reaching null.
Unlike class-based languages that copy inheritance down to instances, JavaScript's model links objects to other objects through a live delegation mechanism. As detailed in the You-Dont-Know-JS repository, specifically in objects-classes/ch2.md, this prototype chain enables objects to share behavior without duplicating data across memory.
What Is the [[Prototype]] Linkage?
Every JavaScript object has an internal, hidden property called [[Prototype]] (also referred to as the prototype chain). When you create an object literal like const myObj = {}, the engine automatically sets its [[Prototype]] to reference Object.prototype.
According to the source code analysis in objects-classes/ch2.md (lines 67-73), this linkage is established at instantiation and serves as the fallback mechanism for property resolution. If a requested property does not exist as an own property on the object, the engine delegates the lookup to the object referenced by [[Prototype]], continuing upward until the property is found or the chain terminates.
How Prototype Delegation Works
The lookup process follows a strict delegation pattern. When you access myObj.toString(), the engine first checks if myObj has an own property toString. If not, it walks up to Object.prototype where toString is defined, as implemented in the specification covered in objects-classes/ch2.md (lines 91-99).
The chain terminates at Object.prototype, whose own [[Prototype]] is null. This prevents infinite lookup loops and defines the top of the inheritance hierarchy.
const person = {
name: 'Alice',
greet() {
return `Hi, I'm ${this.name}`;
}
};
person.greet(); // "Hi, I'm Alice" (own property)
person.toString(); // "[object Object]" (inherited from Object.prototype)
Creating Custom Prototype Links
You can establish custom prototype relationships using Object.create(proto) or the __proto__ literal property introduced in ES6.
Using Object.create()
The Object.create() method creates a new object with its [[Prototype]] explicitly set to the provided object, as documented in objects-classes/ch2.md (lines 44-52):
const animal = {
eats: true,
speak() { return '...'; }
};
const rabbit = Object.create(animal);
rabbit.name = 'Bunny';
rabbit.speak = () => '...hop...';
console.log(rabbit.eats); // true (delegated from animal)
console.log(rabbit.speak()); // "...hop..." (own property shadows prototype)
Using __proto__ Literal Syntax
For object literals, you can specify the prototype directly:
const vehicle = { wheels: 4 };
const car = {
__proto__: vehicle,
brand: 'Toyota'
};
console.log(car.wheels); // 4 (inherited via prototype chain)
Null-Prototype Objects
To create "dictionary" objects that inherit nothing—not even Object.prototype methods—use Object.create(null):
const dict = Object.create(null);
dict.foo = 'bar';
console.log('toString' in dict); // false (no prototype pollution)
This pattern prevents accidental name collisions with inherited properties like constructor or toString.
Constructor Functions and the prototype Property
A critical distinction exists between [[Prototype]] (the internal linkage on instances) and prototype (the public property on constructor functions). As explained in objects-classes/ch2.md (lines 94-102), when you invoke a function with new, the engine sets the new instance's [[Prototype]] to reference the constructor's prototype object.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return `Hello, ${this.name}`;
};
const bob = new Person('Bob');
console.log(bob.greet()); // "Hello, Bob"
console.log(Object.getPrototypeOf(bob) === Person.prototype); // true
Functions themselves also inherit from Function.prototype, which provides methods like call, apply, and bind. This is detailed in objects-classes/ch2.md (lines 16-20).
Live Linkage and Shadowing
The prototype chain maintains a live link to the delegated object. Changes to a prototype immediately affect all objects that delegate to it, unless those objects define their own property that shadows the inherited one:
const base = { kind: 'base' };
const obj = Object.create(base);
console.log(obj.kind); // "base"
base.kind = 'updated';
console.log(obj.kind); // "updated" (live delegation)
obj.kind = 'shadowed';
console.log(obj.kind); // "shadowed" (own property)
console.log(base.kind); // "updated" (unchanged)
Summary
[[Prototype]]is an internal reference that creates the inheritance chain, automatically set toObject.prototypefor object literals.- Delegation allows property lookups to fall back to the prototype chain rather than copying properties during instantiation.
Object.create(proto)and__proto__literals allow explicit configuration of an object's prototype.Object.create(null)generates dictionary objects with zero inheritance, preventing prototype pollution.prototype(on functions) differs from[[Prototype]](on instances); the former becomes the latter when using thenewkeyword.- The chain terminates at
Object.prototype, whose[[Prototype]]isnull.
Frequently Asked Questions
What is the difference between __proto__ and prototype?
__proto__ (or Object.getPrototypeOf()) exposes the internal [[Prototype]] linkage on any object instance, indicating which object it delegates to. In contrast, prototype is a property that exists only on constructor functions (like Person or Array), serving as the template object that becomes the [[Prototype]] of instances created with new.
How do I check if a property is inherited or an own property?
Use the hasOwnProperty method to distinguish own properties from inherited ones. For example, obj.hasOwnProperty('toString') returns false for most objects because toString is inherited from Object.prototype, while obj.hasOwnProperty('customProp') returns true for properties defined directly on the object.
Is JavaScript's prototype chain inheritance slower than class-based inheritance?
Property lookup performance depends on chain depth. Accessing properties on objects with long prototype chains requires walking multiple links, which is marginally slower than direct property access. However, modern JavaScript engines optimize prototype chains heavily, and the memory efficiency of shared methods via delegation often outweighs negligible lookup costs.
Can I change an object's prototype after it is created?
Yes, using Object.setPrototypeOf() or the non-standard __proto__ setter, but this is discouraged for performance reasons. Changing an object's prototype invalidates engine optimizations that assume stable prototype structures. It is better to create a new object with the desired prototype using Object.create() rather than mutating existing prototype links.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →