How JavaScript Prototypes and the Prototype Chain Work: A Complete Guide
JavaScript implements inheritance through a prototype-based system where every object maintains an internal [[Prototype]] reference, and when property access fails on the object itself, the engine traverses the prototype chain upward until it locates the property or reaches null.
The lydiahallie/javascript-questions repository contains detailed explanations of this mechanism, demonstrating how prototypes enable memory-efficient code sharing across object instances. This article breaks down the internal lookup process, constructor function patterns, and dynamic chain modification as documented in the project's README.
What Is the Prototype Chain?
Every JavaScript object has an internal [[Prototype]] property, exposed in modern engines as __proto__ or accessible via Object.getPrototypeOf(). When you access a property like obj.property, the engine performs the following steps:
- Checks if the property exists as an own property on
obj. - If not found, follows the
[[Prototype]]link to the parent object. - Continues walking up the chain until finding the property or reaching
null.
This linked-list structure of objects forms the prototype chain. According to the source code analysis in README.md (Lines 482-483), when a property is missing on the instance, "it goes down the prototype chain and finds it there."
How Constructor Functions Use Prototypes
Functions act as constructors when invoked with the new keyword. Every function object automatically receives a prototype property, which becomes the [[Prototype]] of instances created from that constructor.
Adding methods to a constructor's prototype makes them available to all instances without duplicating the function in memory. As shown in the lydiahallie/javascript-questions README (Lines 404-410), you can define shared behavior like this:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// ✅ shared on the prototype
Person.prototype.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
};
const lydia = new Person('Lydia', 'Hallie');
console.log(lydia.getFullName()); // Lydia Hallie
When lydia.getFullName() executes, JavaScript first checks the lydia instance for the method. Finding none, it follows the prototype link to Person.prototype where the method resides, then invokes it with this bound to the instance.
Modifying and Deleting Prototype Properties
The prototype chain is dynamic. You can add, modify, or remove properties from a prototype object at runtime, and these changes immediately affect all instances linked to that prototype.
Removing a property from a prototype breaks the chain for subsequent lookups. The README illustrates this at Lines 1711-1719:
function Dog() {}
Dog.prototype.bark = function () { console.log('Woof!'); };
const dog = new Dog();
dog.bark(); // Woof!
delete Dog.prototype.bark; // removes the method from the chain
dog.bark(); // TypeError: dog.bark is not a function
Once bark is deleted from Dog.prototype, the lookup mechanism fails to find the method anywhere in the chain, resulting in a TypeError.
Extending Built-in Object Prototypes
Built-in constructors like String, Array, and Object expose their own prototype objects. Extending these prototypes makes new methods available across all instances of that type.
As documented in README.md (Lines 855-859), adding a method to String.prototype works because primitive strings undergo temporary boxing into String objects during property access:
String.prototype.giveLydiaPizza = () => '🍕';
console.log('hello'.giveLydiaPizza()); // 🍕
This technique, while powerful, modifies the global object space and can cause conflicts in large codebases or third-party libraries.
Summary
- Prototype-based inheritance allows objects to inherit features from other objects through the internal
[[Prototype]]link rather than class-based copying. - Constructor functions utilize their
prototypeproperty to share methods across all instances created withnew, optimizing memory usage. - Property lookup traverses the prototype chain from the instance upward until finding the property or reaching
null. - Dynamic modification of prototypes immediately affects all linked instances, including the ability to delete properties and break the chain.
- Built-in prototypes like
String.prototypecan be extended, though this practice requires caution in production environments.
Frequently Asked Questions
How does __proto__ differ from prototype?
The prototype property exists only on constructor functions and serves as the template for instances created with new. The __proto__ accessor (or Object.getPrototypeOf()) exists on all objects and points to the object's immediate parent in the prototype chain. When you call new Person(), the resulting object's __proto__ links to Person.prototype.
Can you break the prototype chain intentionally?
Yes. You can break the chain by setting an object's prototype to null using Object.setPrototypeOf(obj, null) or by deleting properties from intermediate prototypes. Once broken, property lookups that previously succeeded will return undefined or throw TypeError for method calls, as the engine cannot traverse further up the chain.
Why does extending String.prototype work on primitive strings?
JavaScript temporarily boxes primitive values into their object wrappers when you access properties on them. When you call 'text'.method(), the engine converts the primitive string to a String object, looks up method on String.prototype, executes it, then discards the wrapper. This automatic boxing makes prototype extension appear to work on primitives.
What happens when a property exists both on the instance and the prototype?
Instance properties shadow prototype properties. When accessing obj.property, the engine checks the instance first. If the property exists there, it returns the instance value immediately without traversing the chain. This allows individual instances to override shared defaults defined on the prototype.
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 →