JavascriptPrototype-chain

JavaScript Prototype Chain: How Object Inheritance Works

JavaScript Prototype Chain: How Object Inheritance Works

You never wrote map(), yet every array you create knows how to use it. Where does that method come from? The JavaScript prototype chain. It lets one object borrow properties and methods from another, which is how arrays, classes, constructor functions, and reusable object behavior all work without copying the same code into every object.

It is the fallback path JavaScript walks when an object doesn't have the property you asked for:

const animal = {
  eats: true
};

const rabbit = Object.create(animal);
rabbit.name = "Snowball";

console.log(rabbit.name); // "Snowball" — own property
console.log(rabbit.eats); // true — inherited from animal

rabbit has no eats property of its own. JavaScript finds it one step up, on animal, which is rabbit's prototype.

What You'll Learn About the Prototype Chain

Prerequisites for Learning JavaScript Prototypes

What Is the JavaScript Prototype Chain?

The JavaScript prototype chain is the linked sequence of objects JavaScript searches when it resolves a property or method. The search starts at the current object, follows its internal [[Prototype]] links, and stops at null. That is the whole mechanism behind property delegation and prototype-based inheritance.

Ask an object for a property it doesn't have, and JavaScript follows the link to that object's prototype, then keeps walking until it finds the property or runs out of objects.

That last part is the idea worth holding onto, and it's what the rabbit example above shows: objects don't copy anything from their prototypes. They stay connected to them.

Why Does the Prototype Chain Exist?

Prototypes exist so many objects can share the same behavior without each one carrying its own copy. Instead of duplicating methods, an object hands off any property request it cannot answer to another object. Take prototypes away and you'd be copying methods onto every object by hand, or writing your own delegation system.

Think about arrays. Every one of them needs map(), filter(), and push(). Stamping those functions into each new array would burn memory and turn a one-line bug fix into a hunt across thousands of copies.

JavaScript leans on prototypes because objects and functions sit at the center of the language. Shared methods live on a prototype; each object keeps only its own data. Update the shared method once, and every object that inherits it sees the change.

How Does the Prototype Chain Work?

Lookup happens one object at a time. JavaScript checks the original object, then follows its internal [[Prototype]] link, and repeats until it finds a match or hits null, at which point you get undefined.

1

Check the original object

JavaScript checks whether the object has its own property with the requested key.

2

Visit the object's prototype

If the property is missing, JavaScript follows the object's internal [[Prototype]] reference.

3

Continue through linked prototypes

Each prototype is checked in order. The first matching property wins.

4

Stop at null

The final prototype link is null. If no match was found, property access returns undefined.

Here's what that chain looks like for an array:

scores
  ↓ [[Prototype]]
Array.prototype
  ↓ [[Prototype]]
Object.prototype
  ↓ [[Prototype]]
null

Call scores.map(...) and JavaScript normally stops at Array.prototype. Something like toString sits farther up, on Object.prototype, so the search walks one link further to reach it.

Think Like the JavaScript Engine

It helps to trace what the runtime does when your code reads an inherited property. This lookup walks object references, which is a different job from resolving variable names, even though both involve searching.

  • Execution context: The surrounding script or function is already running inside an execution context.
  • Property evaluation: For an expression such as user.describe, the engine evaluates user and checks the object for an own describe property.
  • Prototype traversal: If the property is absent, the engine follows user's [[Prototype]] links until it finds the property or reaches null.
  • Method call: If user.describe() invokes a function, a new function execution context is pushed onto the call stack.
  • this binding: For that method call, this normally refers to user, even when describe was inherited.
  • Memory: Objects and their prototypes stay connected through references. Engines may optimize repeated property lookups internally.
  • Garbage collection: An object can be collected once nothing reaches it anymore. A prototype stays reachable while live objects still point at it.

Two chains, two jobs. The scope chain resolves variable names such as user. The prototype chain resolves properties such as user.name.

MechanismSearches forFollows
Scope chainVariables and function declarationsLexical environments
Prototype chainObject properties and methods[[Prototype]] links

Understanding prototype, [[Prototype]], and __proto__

Three names that look alike and mean different things. This is where most confusion starts, so it's worth separating them:

  • [[Prototype]] is an object's internal link to another object.
  • A function's prototype property, such as User.prototype, is used as the prototype of objects created with new.
  • __proto__ is a legacy accessor that exposes [[Prototype]]; keep it out of application code.
  • Object.getPrototypeOf() safely reads an object's prototype.
  • Object.setPrototypeOf() changes a prototype, though doing so after an object exists can hurt performance.

The example below shows the first two working together: a method on User.prototype, and an instance that reaches it through its own [[Prototype]] link.

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

User.prototype.greet = function greet() {
  return `Hello, ${this.name}`;
};

const user = new User("Mina");

console.log(user.greet()); // "Hello, Mina"
console.log(Object.getPrototypeOf(user) === User.prototype); // true

The chain is:

user → User.prototype → Object.prototype → null

Prototype Chain Examples in JavaScript

With the vocabulary sorted out, here are the three patterns you'll meet most often in real code.

Sharing Methods with Constructor Functions

Put a method on a constructor's prototype and every instance gets it:

function ShoppingCart(owner) {
  this.owner = owner;
  this.items = [];
}

ShoppingCart.prototype.addItem = function addItem(item) {
  this.items.push(item);
};

ShoppingCart.prototype.getTotalItems = function getTotalItems() {
  return this.items.length;
};

const firstCart = new ShoppingCart("Ava");
const secondCart = new ShoppingCart("Noah");

firstCart.addItem("Keyboard");

console.log(firstCart.getTotalItems()); // 1
console.log(secondCart.getTotalItems()); // 0
console.log(firstCart.addItem === secondCart.addItem); // true

That last line is the point: both carts hold separate items arrays, but they share one copy of each method.

Creating Delegation with Object.create()

Object.create() builds an object and sets its prototype in one step:

const accountActions = {
  describe() {
    return `${this.owner} has a ${this.type} account`;
  }
};

const customerAccount = Object.create(accountActions);
customerAccount.owner = "Leah";
customerAccount.type = "savings";

console.log(customerAccount.describe());
// "Leah has a savings account"

console.log(Object.hasOwn(customerAccount, "describe")); // false

describe runs fine even though customerAccount doesn't own it. Nothing is hidden here, which is what makes this pattern good for showing the delegation relationship plainly.

How Classes Use the Prototype Chain

Class syntax reads better, but instance methods still land on a prototype:

class Vehicle {
  move() {
    return "The vehicle is moving";
  }
}

class Bicycle extends Vehicle {
  ringBell() {
    return "Ring ring!";
  }
}

const bicycle = new Bicycle();

console.log(bicycle.ringBell()); // "Ring ring!"
console.log(bicycle.move()); // "The vehicle is moving"

console.log(Object.getPrototypeOf(bicycle) === Bicycle.prototype); // true
console.log(Object.getPrototypeOf(Bicycle.prototype) === Vehicle.prototype); // true

How to Recognize Prototype Chains in Real Code

Once you know the patterns, you start spotting them everywhere. Prototype behavior shows up wherever code reads an inherited property or sets up an inheritance relationship:

  • Constructor functions used with new
  • Assignments such as Constructor.prototype.method
  • class, extends, and super
  • Objects created with Object.create()
  • Calls to Object.getPrototypeOf()
  • Methods used by arrays, dates, maps, sets, and other built-ins
  • Libraries that define reusable methods for many instances

React class components extend React.Component. Custom error types extend Error. Even a plain items.filter() call rides the array prototype chain.

In practice, most production code touches prototypes through classes and built-in objects. Hand-editing prototype links is rare, and usually a smell.

Common Mistakes with the Prototype Chain

Three mistakes account for most prototype bugs. Each one is easy to make and easy to avoid.

Modifying Built-In Prototypes

// ❌ Wrong: affects nearly every ordinary object
Object.prototype.isAvailable = function isAvailable() {
  return true;
};

// ✅ Correct: use a regular utility function
function isAvailable(value) {
  return value !== null && value !== undefined;
}

console.log(isAvailable({})); // true

A plain function does the same work and touches nothing outside its own module.

Storing Mutable Instance Data on a Prototype

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

// ❌ Wrong: shared by all Team instances
Team.prototype.members = [];

const designTeam = new Team("Design");
const engineeringTeam = new Team("Engineering");

designTeam.members.push("Kai");
console.log(engineeringTeam.members); // ["Kai"]

Adding Kai to the design team also added them to engineering. Both teams were pushing into the same array. Create the array per instance instead:

function Team(name) {
  this.name = name;
  this.members = []; // ✅ Each team gets its own array
}

const designTeam = new Team("Design");
const engineeringTeam = new Team("Engineering");

designTeam.members.push("Kai");

console.log(designTeam.members); // ["Kai"]
console.log(engineeringTeam.members); // []

Assuming Every Property Is Owned by the Object

A property can be readable without belonging to the object you're reading it from:

const permissions = {
  canRead: true
};

const userPermissions = Object.create(permissions);
userPermissions.canWrite = false;

console.log("canRead" in userPermissions); // true
console.log(Object.hasOwn(userPermissions, "canRead")); // false
console.log(Object.hasOwn(userPermissions, "canWrite")); // true

Pick your check to match your intent. Use in when inherited properties should count, and Object.hasOwn() when only direct properties should.

Prototype Chain Best Practices

These habits keep you clear of the mistakes above.

A few more worth keeping in mind:

  • Use Object.getPrototypeOf() instead of __proto__.
  • Use Object.hasOwn(object, key) for ownership checks.
  • Keep inheritance chains short and readable.
  • Reach for composition when objects have no clear "is-a" relationship.
  • Leave built-in prototypes alone in shared application code.

Where Prototype Inheritance Is Used in Production

You've been relying on prototype chains all along. They sit under a lot of everyday JavaScript:

  • Built-in collections: Arrays inherit methods from Array.prototype.
  • Custom error types: Application errors can extend the built-in Error class.
  • UI libraries: Class-based components and framework base classes use inheritance.
  • Domain models: AdminUser may extend a general User class.
  • Reusable services: Instances can share validation or formatting methods.
  • Browser APIs: DOM elements inherit methods through several interface prototypes.

The pattern pays off most when many objects need the same stable behavior while holding their own separate state.

JavaScript Prototype Chain Interview Questions

Interviewers keep coming back to the same handful of questions. See how you'd answer these before reading the responses.

Frequently Asked Questions About Prototype Chains

JavaScript Objects

Review object properties, methods, and ownership before working with inheritance.

JavaScript Classes

Learn how class syntax builds on JavaScript's prototype system.

JavaScript Inheritance

Compare inheritance patterns used by constructors, classes, and objects.

Take the Prototype Chain Quiz

Test your understanding of property lookup, prototypes, and inheritance.

🔑 Key Takeaways

Test Your Prototype Chain Knowledge

You can now trace how JavaScript finds an inherited property, share methods across instances, read a class hierarchy as a prototype chain, and explain what happens when a lookup finds nothing. Time to put that against real code.

👉 Test your knowledge with our Prototype Chain Quiz