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 it -
Dog.prototype.bark = ...— adds a method to that shared object, once -
new 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.
Try It: Code Runner
Explore prototypes and prototype lookup behavior below.
Interview Questions
What is a 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: MDN Object prototypes Guide