Under The Hood
frontend javascript ES2015+ (modern prototype semantics)

Prototype Chain: [[Prototype]], __proto__, and Object.create(null)

Last updated
Prerequisites:
JavaScript objects and property lookup
Basics of functions and constructors
  • prototype
  • inheritance
  • prototype-chain
  • object-create
  • proto

Read at your depth

The practical view

Every object has an internal [[Prototype]] (the object it inherits from). Property lookup walks up this chain: obj.method looks on obj, then obj's prototype, then that one's, until found or reaching null. You read/set it with Object.getPrototypeOf / Object.setPrototypeOf, or the modern-ish __proto__ accessor. Object.create(proto) creates a fresh object with a chosen prototype, and Object.create(null) creates an object with NO prototype at all (no toString, no hasOwnProperty). Class syntax is sugar over this: class Dog extends Animal sets Dog.prototype's prototype to Animal.prototype. Property shadowing: defining a same-named property on the object stops the walk (the own property wins).

The same idea in other frameworks

react equivalent
React components use prototype delegation for class components (extends Component), while function components avoid the chain.

Legacy vs modern

Object.create(null) vs a plain object literal for a dictionary

A null-prototype object avoids inherited Object.prototype keys that could collide with or hijack dictionary lookups.

before → after
Plain object literal as a map
const map = {}; // ❌ OLD: inherits from Object.prototype
map['hasOwnProperty'] = 'x'; // shadows an inherited method; lookups can collide
Null-prototype dictionary
const map = Object.create(null); // ✅ NEW: no prototype chain at all
map['hasOwnProperty'] = 'x'; // safe: nothing inherited to collide with

Interview gotchas

Context

Interviewers use this to distinguish developers who truly understand the prototype model from those who only remember syntax — the trio of __proto__, .prototype, and Object.create(null) is a reliable probe.

The mechanical answer

__proto__ is a legacy accessor on Object.prototype that reads/writes the [[Prototype]] of the object it is called on (settable via a setter). .prototype is a plain property that exists only on constructor functions (and classes) — it is the object that becomes the [[Prototype]] of instances created via new. Object.create(null) creates an object whose own [[Prototype]] is literally null, so its chain terminates immediately and it inherits no Object.prototype members. Mutating __proto__ on a normal object changes the live prototype used by the chain, which is why the prototype accessor is discouraged in favor of Object.setPrototypeOf or Object.create.

Trap

The classic trap is conflating __proto__ (on the instance, its prototype) with .prototype (on the constructor, used to build instances) — they point to related but different objects. Another trap: assuming Object.create(null) objects still have toString — they do not, because the chain is empty, so calling map.toString() throws unless you define it yourself.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.