Just JavaScript· 09
Chapter 09 · Objects, in depth

Walking the prototype chain

One more clause completes the property rules. When an object lacks a property, JavaScript doesn't give up — it follows a special __proto__ wire to another object and keeps looking. Reading walks the chain; writing never does. Watch it happen.

A __proto__ is a special wire from one object to another, its prototype. When a property is missing, the search continues there:

let mammal = { furry: true };
let human  = { __proto__: mammal, teeth: 32 };
let gwen   = { __proto__: human,  age: 19 };

console.log(gwen.age);    // 19  — own property
console.log(gwen.teeth);  // 32  — found on human
console.log(gwen.furry);  // true — found on mammal
console.log(gwen.swims);  // undefined — nobody has it

Pick a property in the lab and step the search. Toggle shadowing to give gwen its own teeth and watch the walk stop early.

Lab · walk the chain

How a missing property is resolved, hop by hop

JavaScript checks the object for an own property; if it's missing, it follows __proto__ to the next object and checks again — until it finds the property or runs out of chain (null), which yields undefined.

Step 0 — press Step to begin
ReadyPick a property, optionally add a shadow, then step.
gwen.age
hops0
01

The __proto__ wire

A prototype is just another object. The __proto__ wire says "if you can't find it here, look there next."

This adds one clause to the rules from Properties. When we look up obj.prop and the object has no own prop, we don't immediately answer undefined. First we check whether the object has a __proto__ wire. If it does, we follow it and repeat the search on that object. We keep hopping until the property is found or an object has no prototype (its __proto__ is null) — only then is the result undefined.

The chain is a sequence of objects

gwenhumanmammal → … A prototype chain can be many objects long, but it can never be circular — JavaScript won't let an object end up as its own ancestor.

02

Shadowing: own wins

If an object has its own copy of a property, the search stops there. The prototype's version still exists — it's just hidden.

let human = { teeth: 32 };
let gwen  = { __proto__: human, teeth: 20 };

console.log(gwen.teeth);  // 20  — gwen's own wins
console.log(human.teeth); // 32  — unchanged

gwen.teeth finds an own property immediately and never reaches human. We say gwen's teethshadows human's. The prototype is not modified; the search simply stops earlier. Toggle the shadow in the lab, then re-run gwen.teeth to see the walk end at hop zero.

Own properties only: hasOwnProperty

To ask whether a property belongs to an object itself rather than its chain, use gwen.hasOwnProperty('teeth'). It answers true only for own properties — the prototype's teeth doesn't count.

03

Writing never walks the chain

Reading traverses prototypes. Assignment does not — it always creates or updates an own property on the object you wrote to.

let human = { teeth: 32 };
let gwen  = { __proto__: human };

gwen.teeth = 20;        // creates gwen's OWN teeth
console.log(gwen.teeth);  // 20
console.log(human.teeth); // 32 — prototype untouched

Even though gwen had no own teeth, the assignment did not climb to human and edit it. It created a brand-new own teeth property directly on gwen. From then on, reads of gwen.teeth find that own property first. This is the same shadowing as before — produced by a write rather than a literal.

Two rules of thumb

Readingobj.prop walks the chain until it finds the property or hits null (then undefined). Writingobj.prop = x always sets an own property on obj itself.

04

The Object Prototype

Plain objects aren't the end of the chain. They point at a shared Object Prototype — which is where methods like hasOwnProperty come from.

Make an empty object and inspect its prototype:

let obj = {};
console.log(obj.__proto__); // the Object Prototype
console.log(obj.toString);  // a function — inherited from it

Every object literal's __proto__ points by default at the same special object, the Object Prototype. That's why obj.hasOwnProperty(...) and obj.toString() work on objects you never gave those methods — they're found one hop up the chain. The Object Prototype is the top: its own __proto__ is null, so the search ends there.

You can opt out entirely with an explicit null prototype:

let bare = { __proto__: null };
console.log(bare.toString); // undefined — no prototype to inherit from
Why the lab shows it

Step gwen.swims in the lab: the walk passes human, mammal, then the Object Prototype, then reaches null — and only there does it return undefined.

05

Prototype pollution & the Pineapple Pizza

Because the Object Prototype is shared by almost every object, mutating it affects everything. This is a footgun, not a feature.

If you mutate the Object Prototype, every plain object suddenly appears to have the property you added:

obj.__proto__.smell = 'banana'; // mutates the SHARED Object Prototype

let anything = {};
console.log(anything.smell);    // 'banana' (!)

This is prototype pollution. One mutation leaks into every object whose chain ends at the Object Prototype — which is nearly all of them. It is a classic source of bugs and security holes. Avoid mutating prototypes you don't own.

Think first · the Pineapple Pizza puzzle
let pizza = {};
console.log(pizza.taste); // could this print "pineapple"?
Answer · yes, if the prototype was polluted

pizza has no own taste, so the read walks to its prototype — the Object Prototype. Normally taste isn't there either, so the result is undefined. But if some earlier code ran ({}).__proto__.taste = 'pineapple', that property now lives on the shared prototype, and pizza.taste finds it. The value was never "in" pizza — the rules walked the chain and found it one hop up.

06

__proto__ vs prototype, and recap

Two confusingly-named things. The wire that drives lookups is __proto__; the ordinary property called prototype lives on functions and is used by new.

__proto__ is the lookup mechanism — the wire every object follows when a property is missing. prototype is a regular property that functions happen to have; the new operator and class syntax use it to set up the __proto__ wire of the objects they create. Modern code rarely touches __proto__ directly: classes hide all of this behind cleaner syntax, but underneath, the same chain-walking rules are doing the work.

  • A __proto__ is a wire from an object to its prototype — another object.
  • Reading a missing property follows __proto__ and searches again, until found or null (then undefined).
  • An own property shadows the prototype's; hasOwnProperty checks own only.
  • Writing obj.prop = x never walks the chain — it always sets an own property on obj.
  • Plain objects inherit from the shared Object Prototype (whose __proto__ is null); mutating it pollutes everything.
  • __proto__ (the wire) is not the same as prototype (a property on functions, used by new and classes).
You've finished the model

Wires, values, the JavaScript universe, equality, properties, mutation, and now prototypes — that's the whole mental model. Every expression you read from here on is just these rules, applied carefully.