Prototypes & the Prototype Chain
Every object in JavaScript is secretly connected to another object it can borrow properties and methods from — that hidden connection is called the prototype chain, and it's the real engine behind classes.
What Is a Prototype?
Every object in JavaScript has a hidden link to another object, called its prototype. When you try to read a property that doesn't exist directly on an object, JavaScript doesn't just give up — it automatically checks the object's prototype, then that prototype's prototype, and so on, until it finds the property or runs out of links.
const animal = {
eats: true
};
const rabbit = Object.create(animal); // rabbit's prototype is set to animal
rabbit.jumps = true;
rabbit.eats; // true — not found on rabbit itself, so JS looks up the chain to animal
rabbit.jumps; // true — found directly on rabbit
Think of a prototype chain like inheriting family traits. If someone asks "does Rabbit eat?" and Rabbit doesn't have that trait written down personally, JavaScript checks Rabbit's "parent" (animal) — and if the parent has it, the answer is yes, as if it were Rabbit's own trait all along. This can go up several generations: parent, grandparent, great-grandparent — JavaScript keeps climbing until it finds an answer or runs out of ancestors.
Why Does This System Exist?
Without prototypes, every single object would need its own private copy of every method it uses — wasting memory and making updates painful. Prototypes let many objects share one set of methods, defined only once, instead of duplicating them everywhere.
Every Object Already Has a Prototype
const arr = [1, 2, 3];
arr.push(4); // where does push() actually live?
Object.getPrototypeOf(arr) === Array.prototype; // true!
This answers a question beginners often wonder: array methods like push, map, and filter aren't copied onto every single array you create. They live once, on a shared object called Array.prototype, and every array you make is secretly linked to it. The same is true for strings (String.prototype) and every other built-in type.
Constructor Functions: Prototypes Before Classes
Before the class keyword existed (see Classes: The Basics), developers built reusable "blueprints" using regular functions and prototypes directly:
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
return `${this.name} says woof!`;
};
const rex = new Dog("Rex");
rex.bark(); // "Rex says woof!" — bark isn't on rex itself, it's found via the prototype chain
Dog.prototype— every function automatically comes with a.prototypeobject attached to itDog.prototype.bark = ...— adds a method to that shared object, oncenew Dog("Rex")— creates a new object and links its hidden prototype toDog.prototype- Every dog created this way shares the exact same
barkfunction in memory — it's not duplicated per dog
class Is "Syntactic Sugar" Over This
// This class...
class Dog {
constructor(name) { this.name = name; }
bark() { return `${this.name} says woof!`; }
}
// ...compiles down to roughly the same thing as the constructor-function version above
console.log(typeof Dog); // "function" — a class IS a function under the hood
"Syntactic sugar" means a cleaner way of writing something that works exactly the same underneath. Every method you write inside a class body is automatically placed onto that class's .prototype, just like the manual example above — class just hides that mechanism behind friendlier syntax (see Classes: The Basics and Inheritance & super for the full picture).
Reading and Checking the Chain
const rex = new Dog("Rex");
Object.getPrototypeOf(rex) === Dog.prototype; // true — the "official" way to read an object's prototype
rex.hasOwnProperty("name"); // true — "name" lives directly ON rex
rex.hasOwnProperty("bark"); // false — "bark" lives on the prototype, not on rex itself
"bark" in rex; // true — the `in` operator checks the WHOLE chain, own + inherited
hasOwnProperty() is the key tool for telling the difference between "this object truly owns this property" versus "this object can reach this property through its prototype chain."
Prototypal Inheritance in Classes
class Animal {
speak() { return "Some generic sound"; }
}
class Dog extends Animal {
speak() { return "Woof!"; }
}
const rex = new Dog();
Object.getPrototypeOf(Dog.prototype) === Animal.prototype; // true — extends links the prototype chains together
When one class extends another, JavaScript links their prototypes into a chain automatically. That's why an overridden method (like speak() on Dog) is found first, but if Dog didn't define its own speak(), the lookup would keep climbing to Animal's version instead.
The End of the Chain: null
Object.getPrototypeOf(Object.prototype); // null — the very top of every chain
Every prototype chain eventually ends at Object.prototype (the source of methods like toString() and hasOwnProperty() that nearly every object has access to), whose own prototype is finally null — signaling "there's nothing further up; stop looking."
Common Mistakes
- Confusing
__proto__(the actual hidden link on an instance) with.prototype(a property that only exists on functions, used to build that link) — they're related but not the same thing. - Adding methods directly inside a constructor function instead of on
.prototype— this creates a brand new copy of that function for every single instance, wasting memory instead of sharing one copy. - Assuming
for...inonly shows an object's own properties — it also walks up the prototype chain and includes inherited enumerable properties, which can surprise you.
Best Practices
- Prefer
classsyntax for new code — it does the same prototype work under the hood, but is far easier to read and less error-prone than manual prototype wiring. - Use
Object.create(null)when you want a truly "bare" object with no inherited methods at all, useful for simple lookup maps. - Use
hasOwnProperty()(orObject.hasOwn()) when you specifically need to know if a property is the object's own, not inherited.
Performance Notes
Looking a property up through a long prototype chain is slightly slower than finding it directly on the object, since the engine has to check each link in turn — but modern engines optimize this heavily, so it's rarely a real bottleneck. Sharing methods via the prototype (rather than duplicating them per instance) is actually a memory-saving optimization, not a cost.
Browser Compatibility
Prototypes are a foundational part of JavaScript and have existed since the language's first version. Object.create(), Object.getPrototypeOf(), and the class keyword are more modern (ES5/ES6) but supported in all current browsers.
Interview Questions
What is the prototype chain?
A series of linked objects that JavaScript searches through, one level at a time, whenever a property or method isn't found directly on an object — allowing objects to share and inherit behavior without duplicating it.
Is JavaScript's class keyword a completely different inheritance system from prototypes?
No — class is syntactic sugar built entirely on top of the existing prototype system. Methods defined in a class body are placed onto that class's prototype object, exactly as they would be with manual constructor-function code.
✏️ Exercise
Create a constructor function Book(title) and add a method describe() to Book.prototype that returns a sentence using this.title. Create two books and confirm they share the exact same describe function using ===.
🧠 Quiz
1. What does JavaScript do when a property isn't found directly on an object?
It checks the object's prototype, then that prototype's own prototype, continuing up the chain until the property is found or the chain ends at null.
2. What sits at the very top of every prototype chain?
Object.prototype, whose own prototype is null, marking the end of the chain.
🚀 Mini Challenge
Using constructor functions (not the class keyword), build a Shape "parent" with an area() method returning 0, and a Square "child" whose prototype chain links back to Shape, overriding area() to return this.side * this.side. Verify with Object.getPrototypeOf() that the link is set correctly.
Summary
- Every object has a hidden link to another object (its prototype), forming a chain JavaScript searches when a property isn't found locally.
- Built-in methods like array methods live once on a shared prototype, not duplicated per instance.
classis a cleaner syntax built entirely on top of this same prototype mechanism.
Related Topics
MDN reference: developer.mozilla.org → JavaScript → Object prototypes (placeholder — add live link)