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.
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.
const obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // → true
console.log(Object.getPrototypeOf(Object.prototype)); // → null
The chain for an array
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
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
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
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.