← All concepts
Object-Oriented Programming

Prototypes and Inheritance

When an object does not have a property itself, JavaScript looks for it on the object's prototype instead.

Definition

Every JavaScript object has an internal link to another object called its prototype, accessible through `Object.getPrototypeOf(obj)` or the legacy `__proto__` accessor. When you read a property, the engine first checks the object's own properties. If the property is not there, it does not give up, it follows the prototype link and checks that object's own properties, then that object's prototype, and so on. This sequence of linked objects is the prototype chain, and it ends at `Object.prototype`, whose own prototype is `null`. If the chain runs out with no match, the read returns `undefined` instead of throwing. This is how methods are shared without copying them onto every object. A constructor function's methods usually live on `Constructor.prototype`, one shared object, and every instance created with `new Constructor()` gets its `[[Prototype]]` set to that same object. So `instance.method()` works even though `method` is never an own property of `instance`, the lookup walks up to the prototype and finds it there. Own properties always win over inherited ones: if an instance defines its own version of a name, the chain walk stops at the instance and the prototype's version is never reached.

Examples

function Dog(name) {
  this.name = name;
}
Dog.prototype.speak = function () {
  return this.name + " barks";
};
const rex = new Dog("Rex");
rex.speak(); // "Rex barks"

rex has no own speak property, so the lookup walks up rex's prototype link to Dog.prototype, finds speak there, and calls it with this bound to rex.

const rex = { name: "Rex" };
const animal = { legs: 4 };
Object.setPrototypeOf(rex, animal);
rex.legs; // 4, found on the prototype
rex.hasOwnProperty("legs"); // false

legs is not an own property of rex, but the prototype chain reaches animal and finds it there, while hasOwnProperty correctly reports that rex itself never had it.

function Base() {}
Base.prototype.greet = () => "hi";
const obj = {};
Object.setPrototypeOf(obj, Base.prototype);
obj.missing; // undefined, chain reaches Object.prototype then null

When no object in the chain has the property, the walk reaches Object.prototype, then its prototype null, and the read quietly returns undefined.

Common mistakes

  • Assuming a missing property throws an error; the lookup just keeps walking the chain and returns undefined once it reaches null.
  • Confusing an instance's own properties with inherited ones, for (const key in obj) walks the whole chain unless you check hasOwnProperty.
  • Mutating a shared prototype method by accident, since every instance points at the same prototype object, a change there affects all of them at once.

Key takeaways

  • Property lookup walks the prototype chain: the object itself, then its [[Prototype]], and so on up to Object.prototype, then null.
  • A property that is not found anywhere in the chain returns undefined instead of throwing.
  • Shared methods live once on a prototype, not copied onto every instance, which is why arrays all share methods like map.
  • An object's own property always shadows a same-named property found further up the chain.
Check your understanding

const a = { x: 1 }; const b = Object.create(a); b.y = 2; console.log(b.x, b.y, a.y); What does this log?

Where you see this

  • Understanding why every array has methods like map and push without those methods being copied onto each array.
  • Debugging why a method call works on an object that never explicitly defined that method.
  • Reasoning about memory: shared prototype methods cost one function per class, not one per instance.
  • Recognizing why own properties (like an instance field) shadow a same-named prototype method.
  • Understanding how class syntax and Object.create both build on this same underlying mechanism.

Practice this

Further reading

Previous
DOM Events and Delegation
Next
ES6 Classes