Prototypes & Prototype Chain

JavaScript's real inheritance system — how objects inherit from other objects

Last updated on

Prototypes are the most important interview topic in JavaScript. The class syntax is just sugar — under the hood, everything is prototypes.

"Prototype tells WHERE methods are searched."

What is a Prototype?

Every JavaScript object has a hidden internal property called [[Prototype]] (accessible via __proto__ or Object.getPrototypeOf()). When you access a property that doesn't exist on an object, JavaScript looks up the prototype chain.

const user = { name: "Shiva" };

// user doesn't have toString(), but it works:
user.toString(); // "[object Object]"

// Because JavaScript found it on Object.prototype:
Object.getPrototypeOf(user) === Object.prototype; // true

The Prototype Chain

user (own property: name)
  ↓ __proto__
Object.prototype (toString, hasOwnProperty, valueOf, etc.)
  ↓ __proto__
null (end of the chain)
const arr = [1, 2, 3];

// arr → Array.prototype → Object.prototype → null
arr.push(4);        // found on Array.prototype
arr.toString();     // found on Object.prototype
arr.nonExistent;    // undefined (searched all the way to null)

__proto__ vs prototype

This is the most confusing part. Here's the clear distinction:

__proto__prototype
Exists onEvery objectOnly functions
What it isLink to parent objectTemplate for instances created with new
Usageobj.__proto__Function.prototype
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function () {
  return `Hi, I'm ${this.name}`;
};

const shiva = new Person("Shiva");

// shiva.__proto__ === Person.prototype
// Person.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
shiva                     Person.prototype         Object.prototype
{ name: "Shiva" }    →   { greet: fn }        →   { toString: fn, ... }   → null
      __proto__                __proto__                 __proto__

Creating Prototypal Inheritance

Using Object.create()

const animal = {
  eat() {
    return `${this.name} is eating`;
  }
};

const dog = Object.create(animal); // dog's prototype = animal
dog.name = "Rex";
dog.bark = function () {
  return "Woof!";
};

dog.eat();  // "Rex is eating" — found on animal (prototype)
dog.bark(); // "Woof!" — found on dog (own property)

Using Constructor Functions

function Animal(name) {
  this.name = name;
}

Animal.prototype.eat = function () {
  return `${this.name} is eating`;
};

function Dog(name, breed) {
  Animal.call(this, name); // call parent constructor
  this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // fix constructor reference

Dog.prototype.bark = function () {
  return "Woof!";
};

const rex = new Dog("Rex", "Lab");
rex.eat();  // "Rex is eating" — from Animal.prototype
rex.bark(); // "Woof!" — from Dog.prototype

Property Shadowing

When an object has its own property that matches a prototype property:

const parent = { type: "parent", greet() { return "parent greet"; } };
const child = Object.create(parent);

child.type = "child"; // shadows parent.type

console.log(child.type);        // "child" (own property)
console.log(child.greet());     // "parent greet" (from prototype)

delete child.type;
console.log(child.type);        // "parent" (falls back to prototype)

Method Lookup Performance

JavaScript searches the prototype chain every time you access a property. Own properties are fastest:

// Fast: own property
const obj = { x: 1 };
obj.x; // found immediately

// Slower: prototype property
const child = Object.create(obj);
child.x; // searches child first, then obj

// Use hasOwnProperty to check
obj.hasOwnProperty("x");       // true
child.hasOwnProperty("x");     // false
Object.hasOwn(child, "x");     // false (ES2022)

for...in and Prototypes

for...in iterates over all enumerable properties, including inherited ones:

const parent = { a: 1 };
const child = Object.create(parent);
child.b = 2;

for (const key in child) {
  console.log(key); // "b", "a" (includes inherited!)
}

// Filter to own properties only
for (const key in child) {
  if (Object.hasOwn(child, key)) {
    console.log(key); // "b" only
  }
}

// Better: use Object.keys (only own enumerable)
Object.keys(child); // ["b"]

Interview Questions

Q: What's the difference between __proto__ and prototype?

Answer: __proto__ is a property on every object that points to its parent in the prototype chain. prototype is a property on constructor functions that becomes the __proto__ of instances created with new.

Q: What's at the end of every prototype chain?

Answer: null. Object.prototype.__proto__ is null.

Q: How does method lookup work?

Answer: When you access a property, JavaScript first checks the object itself. If not found, it checks __proto__, then __proto__.__proto__, and so on until it reaches null. If still not found, it returns undefined.

On this page