The Prototype Chain
Go beneath class syntax to understand how JavaScript really shares behavior between objects — the internal [[Prototype]] link, prototype-based method sharing, Object.create(), and how instanceof works.
Reading Time
18 min
Lesson
Lesson 38 of 48
Accessing the Prototype: getPrototypeOf and __proto__
You can't reach [[Prototype]] directly, but you can inspect and change it. Object.getPrototypeOf(obj) is the standard, modern way to read an object's prototype, and Object.setPrototypeOf(obj, newProto) lets you change it (though doing so hurts performance and is rarely necessary). You may also encounter obj.__proto__, a legacy getter/setter that does the same thing — it works in every browser for compatibility reasons, but it was never part of the language's core design the way Object.getPrototypeOf() is, and modern code should prefer the function form.
Inspecting the Prototype Link
Console Output
Click “Run” to see the console output here.
How Property Lookup Walks the Chain
When you write rabbit.eats, the JavaScript engine first checks whether rabbit itself has an own property called eats. It doesn't, so the engine moves to Object.getPrototypeOf(rabbit) — which is animal — and checks there instead. animal does have an eats property, so that value is returned. If animal hadn't had it either, the engine would have continued up to Object.getPrototypeOf(animal), which is Object.prototype, and checked there, and so on until it hit null. This walk happens on every property and method lookup, automatically and invisibly, which is exactly how objects appear to 'inherit' behavior from other objects without copying anything.
Own Properties vs. Inherited Properties
Console Output
Click “Run” to see the console output here.
Constructor Functions and .prototype
Before class syntax existed — and still, underneath it — JavaScript shared methods between many objects using constructor functions together with a special property every function has: .prototype. Note the distinction: [[Prototype]] is the internal link an object has to another object, while .prototype is an ordinary, visible property that only functions have, holding the object that will become the [[Prototype]] of every instance created from that function with new. Placing a method on Fn.prototype means every instance shares that exact same function in memory, rather than each instance carrying its own separate copy.
Object.create(): Building an Object with a Chosen Prototype
Object.create(proto) creates a brand-new, empty object and directly sets its [[Prototype]] to whatever object you pass in — no constructor function, no new keyword required. This is the most explicit way to set up prototype-based sharing: you build one object holding the shared behavior, then use Object.create() to produce as many objects linked to it as you like. Passing null creates an object with no prototype at all — not even Object.prototype — which is occasionally useful for a plain data dictionary that should never accidentally inherit anything, not even built-ins like toString().
Object.create() in Practice
Console Output
Click “Run” to see the console output here.
class is Sugar Over This Same Mechanism
The classes lesson mentioned that class syntax is just a friendlier way of writing the constructor-function-plus-prototype pattern — this is that mention, followed all the way through. When you write a method inside a class body, JavaScript places it on ClassName.prototype, exactly like manually assigning Fn.prototype.method = function () {...}. When you call new ClassName(), the engine creates a new object, sets its [[Prototype]] to ClassName.prototype, runs the constructor with this bound to that new object, and returns it — identical, step for step, to what new does with an ordinary constructor function. class doesn't introduce a new object model; it introduces stricter syntax (like throwing if you forget new) around the same prototype chain that's been there all along.
Proving class Uses the Same Prototype Mechanism
Console Output
Click “Run” to see the console output here.
instanceof and the Prototype Chain
The instanceof operator answers the question 'is Constructor.prototype anywhere in this object's prototype chain?' obj instanceof Constructor walks up obj's chain of [[Prototype]] links, comparing each one against Constructor.prototype, and returns true the moment it finds a match — or false if it reaches the end of the chain (null) without one. This is also why instanceof can give surprising answers if a prototype is reassigned after instances were already created, since it always checks the chain as it currently stands, not as it was at creation time.
instanceof in Action
Console Output
Click “Run” to see the console output here.
Prototype Terminology at a Glance
| Term | What It Is |
|---|---|
| [[Prototype]] | An internal slot every object has, linking to another object or null |
| Object.getPrototypeOf(obj) | The standard way to read an object's [[Prototype]] link |
| obj.__proto__ | A legacy getter/setter for the same link — works everywhere, but prefer the function form |
| Fn.prototype | An ordinary property that only functions have — becomes new instances' [[Prototype]] |
| Object.create(proto) | Creates a new object with its [[Prototype]] set directly to proto |
| instanceof | Checks whether a constructor's .prototype appears anywhere in an object's chain |
Don't Confuse .prototype with [[Prototype]]
This is the single most common source of confusion on this topic. Fn.prototype is a plain, visible property that exists on functions, used as the template for instances created with new. [[Prototype]] is the internal, hidden link every object (including Fn.prototype itself) has to its own prototype. When you write new Fn(), the new instance's [[Prototype]] is set to Fn.prototype — two different things, one becoming the value of the other.
Reach for Object.getPrototypeOf(), Not __proto__
__proto__ works almost everywhere for historical reasons, but it was standardized late and only for web compatibility — it was never meant to be the primary API. Object.getPrototypeOf() and Object.setPrototypeOf() are the methods actually designed for this, and they read clearly as intentional prototype inspection rather than a mysterious double-underscore property.