Skip to content

JavaScript Tutorial Prototypes

The Prototype Chain

Lesson 197 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

A prototype can have its own prototype, which can have another, and so on. The sequence is the prototype chain. When you read a property, JavaScript walks the chain until it finds it, or reaches the end.

script.js
JS
const grandparent = { family: "Paswan" };
const parent = Object.create(grandparent);
parent.job = "teacher";
const child = Object.create(parent);
child.name = "Maya";

console.log(child.name);    // → Maya     (own property)
console.log(child.job);     // → teacher  (from parent)
console.log(child.family);  // → Paswan   (from grandparent)
console.log(child.pet);     // → undefined (reached the end, nothing found)

The lookup order for child.family was: child → parent → grandparent → found.

Where the chain ends

The chain finishes at null. Almost every chain passes through Object.prototype just before that.

script.js
JS
const obj = {};

console.log(Object.getPrototypeOf(obj) === Object.prototype); // → true
console.log(Object.getPrototypeOf(Object.prototype));          // → null

The chain for an array

script.js
JS
const list = [1, 2, 3];

console.log(Object.getPrototypeOf(list) === Array.prototype);                  // → true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype);      // → true
console.log(Object.getPrototypeOf(Object.prototype));                          // → null
code
TEXT
list  →  Array.prototype  →  Object.prototype  →  null
          (map, filter...)    (toString, hasOwnProperty...)

That is why an array can use both map (from Array.prototype) and hasOwnProperty (from Object.prototype).

Functions are objects too

script.js
JS
function greet() {}

console.log(Object.getPrototypeOf(greet) === Function.prototype); // → true
console.log(typeof greet.call);                                    // → function

call, apply and bind live on Function.prototype.

Own vs inherited properties

script.js
JS
const parent = { inherited: 1 };
const child = Object.create(parent);
child.own = 2;

console.log(Object.keys(child));              // → ["own"]      (only own, enumerable)
console.log("inherited" in child);            // → true         (in checks the whole chain)
console.log(Object.hasOwn(child, "inherited")); // → false

for (const key in child) {
  console.log(key);                           // own, inherited  (for...in includes inherited)
}

This is why for...in can surprise you, see for…in.

Performance and depth

Long chains make lookups slightly slower, and make code harder to follow. Keep them short.

Seeing the chain in the console

Log an object in the browser console and expand it. The [[Prototype]] entry shows its prototype, and you can follow the chain by clicking through.

lightbulb
Remember
Reading a property walks up the chain. Writing a property always stays on the object itself.