Skip to content
Lesson 2 of 8

Step 1 of 5 · Reading · ~3 min

Learn

Iteration and Generators

The Prototype Chain

JavaScript Intermediate showed you that class is sugar over a chain of prototype objects and that instanceof walks it. This lesson opens the machinery: what new does step by step, the exact rule instanceof applies, why reads walk the chain but writes do not, and how that asymmetry causes shared-state and security bugs. By the end you can predict where any property lives.

What new does

new Counter(5) on a function constructor performs four steps:

  1. Create an empty object whose [[Prototype]] is Counter.prototype.
  2. Call Counter with this set to that object and the arguments you passed.
  3. If Counter returned an object, that object is the result of the whole expression.
  4. Otherwise the object from step 1 is the result.

Step 3 is the one people miss. A return value that is a primitive is ignored:

javascript

A class constructor refuses step 2: calling it without new throws a TypeError, because the engine builds this itself. So only plain functions can be re-created by hand.

The rule behind instanceof

x instanceof C is true when the object C.prototype appears somewhere on x's chain. Start at Object.getPrototypeOf(x), move one link at a time, and stop at null. Two cases never enter the walk: if x is not an object (a number, a string, null) the answer is false straight away, and an object made with Object.create(null) has an empty chain, so it is an instance of nothing, not even Object.

Reads walk the chain, writes do not

A read of obj.key visits obj, then its prototype, and so on until some object owns key. An assignment to obj.key normally creates or updates an own property of obj and leaves the prototype alone:

javascript

Two exceptions come from property descriptors. An inherited setter runs instead of creating an own property, with this set to the object you assigned on. An inherited property with writable: false makes the assignment fail (a TypeError in strict mode, silently ignored otherwise).

Shared state on a prototype

Every instance sees the same prototype object, so a mutable value stored there is shared:

javascript

Put per-instance data in the constructor (this.items = []), and keep only functions and constants on the prototype.

Prototype pollution

__proto__ is an accessor inherited from Object.prototype. Code that copies keys with target[k] = value can therefore be tricked into writing to Object.prototype itself, and every object in the program then inherits the new property:

javascript

Defences: skip the keys __proto__, constructor and prototype when merging, build lookup tables with Object.create(null), or use a Map.

Common mistakes

  • Putting an array or object literal on a prototype and expecting each instance to get its own copy.
  • Using the loop for (k in obj) to list own keys. It includes inherited enumerable ones; use Object.keys or Object.prototype.hasOwnProperty.call(obj, k).
  • Calling obj.hasOwnProperty(k) on an Object.create(null) object. It has no such method and throws.
  • Assuming x instanceof Object is true for every value. Primitives and null-prototype objects answer false.

Your exercise

You will rebuild new, instanceof and an "who owns this property" lookup, then run them against a small Animal/Dog/Puppy hierarchy. The usual slip is missing step 3 above, or letting a primitive like 5 reach Object.getPrototypeOf, which quietly answers for it.

Up nextProxy and ReflectProxy and Memory

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…