Starting from the array literal `[]`, which objects does its prototype chain link to, and what terminates the chain?
answer
- it is a finite linked list
- three hops for an array
- one shared root object for almost everything
- the terminator is not an object
- null ends it after Object.prototype
basics
~10 sAn array links to Array.prototype, which links to Object.prototype, which links to null. Null terminates every ordinary prototype chain, so Object.prototype is the last real object a lookup can reach.
solid answer
~40 s`Object.getPrototypeOf([])` is `Array.prototype`, `Object.getPrototypeOf(Array.prototype)` is `Object.prototype`, and `Object.getPrototypeOf(Object.prototype)` is `null`. That `null` is the terminator: the chain is a finite linked list of objects ending in a non-object, which is why a lookup for a name nobody defines finishes in bounded time and yields `undefined`. `Array.prototype` supplies `map`, `filter` and friends; `Object.prototype` supplies the universal members `toString`, `valueOf` and `hasOwnProperty`, which is why practically every value appears to have them. Functions chain the same way through `Function.prototype` to `Object.prototype` to `null`. Only an object deliberately created with a `null` prototype has an empty chain, and that is precisely why it lacks `toString` and `hasOwnProperty`.
code
javascript · 8 linesconst a = [];
console.log(Object.getPrototypeOf(a) === Array.prototype); // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
// functions take one different first hop
console.log(Object.getPrototypeOf(function f() {}) === Function.prototype); // truego deeper
Know that an array gets its methods from Array.prototype rather than owning them, and that following prototype links eventually reaches Object.prototype and then null.
Be able to name each hop for arrays, functions and built-ins, explain that null is the terminator that makes lookup finite, and read the chain with Object.getPrototypeOf.
Use the chain as a diagnostic — explain a missing or unexpectedly present method by where it sits, and recognise when an object was built with no chain at all.
Own the consequence at scale: Object.prototype is a single global root every ordinary object shares, so anything written there is a program-wide change, which drives both the null-prototype dictionary idiom and the risk of prototype mutation.
## The chain is a linked list An object's prototype link is a single reference — to another object, or to `null`. Following those references from any starting object produces a finite list, the *prototype chain*. For an array literal it is three hops long: ```js const a = []; const p1 = Object.getPrototypeOf(a); // Array.prototype const p2 = Object.getPrototypeOf(p1); // Object.prototype const p3 = Object.getPrototypeOf(p2); // null ``` Each link explains a group of capabilities. `Array.prototype` is where `map`, `filter`, `push`, `slice` and `includes` actually live — an array literal owns only its indexed elements and `length`, and borrows everything else. `Object.prototype` is where `toString`, `valueOf`, `hasOwnProperty`, `isPrototypeOf` and `propertyIsEnumerable` live, which is why almost every value in the language appears to have them. ## Why `null` matters `null` is not an object and has no prototype link, so the walk stops there. That termination is what makes lookup total: the engine either finds the name on some object in the list or runs off the end and produces `undefined` — never an infinite walk. It is also why the language rejects prototype cycles: `Object.setPrototypeOf(a, a)` throws a `TypeError` rather than building a list with no end. ```js const a = {}; Object.setPrototypeOf(a, a); // TypeError: Cyclic __proto__ value ``` ## The same shape for every built-in Every built-in type follows the identical pattern — an instance links to its constructor's `.prototype`, and that object links to `Object.prototype`: ```js Object.getPrototypeOf(new Date()) === Date.prototype; // true Object.getPrototypeOf(/x/) === RegExp.prototype; // true Object.getPrototypeOf(new Map()) === Map.prototype; // true Object.getPrototypeOf(Date.prototype) === Object.prototype; // true ``` Functions are the mild exception in that their first hop is `Function.prototype`, itself a callable object, before landing on `Object.prototype`: ```js Object.getPrototypeOf(function f() {}) === Function.prototype; // true Object.getPrototypeOf(Function.prototype) === Object.prototype; // true typeof Function.prototype; // 'function' ``` And `Object` itself, being a function, links to `Function.prototype` — do not confuse `Object` (the constructor function) with `Object.prototype` (the root object). `Object.getPrototypeOf(Object)` is `Function.prototype`, not `Object.prototype`. ## Primitives borrow a chain Writing `'abc'.toUpperCase()` works even though a string is not an object: the language wraps the primitive momentarily, and the wrapper's chain is `String.prototype` → `Object.prototype` → `null`. Since ES2015, `Object.getPrototypeOf` accepts a primitive and coerces it, returning `String.prototype` for a string; in ES5 the same call threw a `TypeError`, which is the only version wrinkle worth knowing here. ```js Object.getPrototypeOf('abc') === String.prototype; // true (ES2015+) ``` `null` and `undefined` have no wrapper and no chain at all — that is exactly why `undefined.toString()` throws. ## Chains that start at `null` An object can be created with `null` as its prototype instead of `Object.prototype`. Its chain is empty, so it has none of the universal members: `obj.toString` is `undefined`, `obj.hasOwnProperty` is `undefined`, and template-string interpolation of it throws because there is no `toString` to call. That is a deliberate trade for a pure key/value bag, and it is the clearest demonstration that `Object.prototype` is a normal object in a normal chain rather than a hard-wired part of every value. ## Reading a chain in practice When a method mysteriously exists (or does not), walking the chain is the diagnostic. A short loop makes it visible: ```js function chainOf(value) { const out = []; let current = Object.getPrototypeOf(value); while (current !== null) { out.push(current.constructor ? current.constructor.name : '(no constructor)'); current = Object.getPrototypeOf(current); } return out; } chainOf([]); // ['Array', 'Object'] ``` The loop terminates for the same reason lookup does: `null` is always the last link.
- What is `Object.getPrototypeOf(Object)`, and why does it surprise people?`Function.prototype`. `Object` is the constructor function, and every function inherits from `Function.prototype`, so that is its link. People expect `Object.prototype` because the names look related, but `Object.prototype` is the object `Object` hands to instances — a completely different object that sits at the root of the chain. `Object.getPrototypeOf(Object.prototype)` is `null`.
- How does an object with a `null` prototype behave differently, and where is that useful?It has no inherited members at all: no `toString`, no `hasOwnProperty`, no `valueOf`, so string interpolation of it throws and you must call `Object.hasOwn(obj, key)` or `Object.prototype.hasOwnProperty.call(obj, key)` externally. That is the point — as a pure lookup table it cannot collide with inherited names and cannot be reached by anything walking up to `Object.prototype`.
- Why does `'abc'.toUpperCase()` work when a string primitive is not an object?Method access on a primitive briefly consults the corresponding wrapper's prototype chain, so `toUpperCase` is found on `String.prototype`, which links to `Object.prototype` and then `null`. Since ES2015, `Object.getPrototypeOf('abc')` coerces and returns `String.prototype` rather than throwing as it did in ES5. `null` and `undefined` have no wrapper and no chain, which is why calling anything on them throws.
saying these in an interview costs you the question
- Says the chain ends at Object rather than null
- Thinks Object.getPrototypeOf(Object) is Object.prototype
- Believes arrays own map and filter directly
- Claims chains can be circular
- Assumes every object inherits from Object.prototype