# JavaScript Prototype Chain Explained: How Inheritance Works

Source: https://devdocs10.com/articles/javascript/js-prototypes  
By Murat Bo · Published 2026-07-09 · DevDocs10 (https://devdocs10.com)

The prototype chain is the mechanism JavaScript uses to look up properties and methods on objects beyond the object itself. Understanding it helps you read object-oriented JavaScript, debug missing properties, and use built-in objects correctly.

> **Quick answer:** When you access a property on an object, JavaScript first checks the object itself. If it does not find the property there, it follows the prototype chain to the object’s prototype, then the prototype’s prototype, and so on until it reaches `null`.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic JavaScript objects, how dot notation works, and the difference between values stored on an object versus values inherited from somewhere else.

## 1. What Is the Prototype Chain?

The prototype chain is a linked series of objects that JavaScript uses for property lookup. Every object can have a prototype, and that prototype can have its own prototype, forming a chain.

- An object stores its own properties directly.
- If a property is missing, JavaScript searches the prototype.
- If it is still missing, the search continues up the chain.
- The chain ends at `null`.

This is the foundation of JavaScript inheritance. It is also why arrays have methods like `push()` and strings can use methods like `slice()` even though those methods are not stored on each individual value.

## 2. Why the Prototype Chain Matters

The prototype chain is important because it explains how JavaScript objects share behavior without copying methods into every instance. That keeps code memory-efficient and consistent.

It also matters when you are debugging. A property may appear to “exist” on an object even though it is inherited from a prototype. Likewise, a property lookup may fail because the property is not on the object you think it is.

## 3. Basic Syntax

### Property lookup flow

JavaScript does not search randomly. It follows a predictable order: first the object itself, then its prototype, then the prototype’s prototype, and so on.

```javascript
const user = { name: "Ava" };
```

The object above has its own `name` property. If you ask for a property that is not there, JavaScript will look at the prototype chain next.

### Inspecting a prototype

```javascript
const proto = Object.getPrototypeOf(user);
```

This returns the prototype object that `user` delegates to for missing properties.

### Creating an object with a known prototype

```javascript
const animal = {
  speak() {
    return "Some sound";
  }
};

const dog = Object.create(animal);
dog.name = "Milo";
```

Here, `dog` inherits `speak()` from `animal`. The method is not copied; it is found through the prototype chain.

## 4. Step-by-Step Examples

### Example 1: Looking up an own property

When a property exists directly on the object, JavaScript stops immediately and uses it.

```javascript
const book = {
  title: "Clean Code"
};

console.log(book.title);
```

Output (tested with Node 24.21.0):

```text
Clean Code
```

The lookup ends on `book` itself because `title` is an own property.

### Example 2: Falling back to the prototype

Now compare that with a property that is inherited.

```javascript
const person = {
  greet() {
    return "Hello";
  }
};

const admin = Object.create(person);
admin.role = "editor";

console.log(admin.greet());
```

Output (tested with Node 24.21.0):

```text
Hello
```

`admin` does not define `greet()`, so JavaScript finds it on `person`.

### Example 3: Built-in objects use the prototype chain

Built-in objects also rely on prototypes. Array instances inherit methods from `Array.prototype`.

```javascript
const items = [1, 2, 3];

console.log(items.map(n => n * 2));
```

Output (tested with Node 24.21.0):

```text
[ 2, 4, 6 ]
```

The `map()` method comes from the array prototype, not from the `items` array object itself.

### Example 4: Checking whether a property is inherited

Use `hasOwnProperty` or `Object.hasOwn` when you need to know whether a property belongs directly to the object.

```javascript
const vehicle = {
  wheels: 4
};

const car = Object.create(vehicle);
car.brand = "Toyota";

console.log(Object.hasOwn(car, "brand"));
console.log(Object.hasOwn(car, "wheels"));
```

Output (tested with Node 24.21.0):

```text
true
false
```

This distinguishes direct properties from inherited ones, which is very useful during debugging.

## 5. Practical Use Cases

- Sharing methods across many objects without duplicating them.
- Understanding built-in behavior such as arrays, dates, and regular expressions.
- Checking whether a property is direct or inherited when validating data.
- Building constructor functions and classes that reuse shared behavior.
- Debugging why a property or method is available even though it was never assigned on the object itself.

## 6. Common Mistakes

### Mistake 1: Assuming inherited properties are own properties

Beginners often see a property on an object and assume it belongs directly to that object. That can lead to incorrect validation logic.

**Problem:** The property exists on the prototype, so a simple property check can give misleading results if you expect only direct properties.

```javascript
const settings = Object.create({ theme: "dark" });

if ("theme" in settings) {
  console.log("theme exists");
}
```

Output (tested with Node 24.21.0):

```text
theme exists
```

**Fix:** Use `Object.hasOwn` when you only want properties that are directly on the object.

```javascript
const settings = Object.create({ theme: "dark" });

if (Object.hasOwn(settings, "theme")) {
  console.log("own theme exists");
}
```

The corrected version works because it checks only the object’s own properties, not inherited ones.

### Mistake 2: Forgetting where methods actually live

It is easy to believe that methods like `map()` or `toUpperCase()` are stored on each value. In reality, they come from prototypes.

**Problem:** Trying to call a method on the wrong type often leads to errors such as `TypeError: value.map is not a function` because the object does not inherit that method.

```javascript
const notAnArray = {
  0: 1,
  1: 2,
  length: 2
};

notAnArray.map(n => n * 2);
```

**Fix:** Use a real array when you need array methods, or convert array-like data first.

```javascript
const values = [1, 2];

console.log(values.map(n => n * 2));
```

Output (tested with Node 24.21.0):

```text
[ 2, 4 ]
```

The fixed version works because arrays inherit `map()` from `Array.prototype`.

### Mistake 3: Mutating shared prototype objects by accident

If several objects share the same prototype object, changing a nested object on that prototype affects every object that inherits from it.

**Problem:** This is not a JavaScript syntax error, but it is a common design bug. One change can appear everywhere because all children reference the same prototype object.

```javascript
const defaults = {
  options: { debug: false }
};

const first = Object.create(defaults);
const second = Object.create(defaults);

first.options.debug = true;
```

**Fix:** Put per-object state on the object itself, not on shared prototype data structures.

```javascript
const defaults = {
  log() {
    console.log("debug mode");
  }
};

const first = Object.create(defaults);
const second = Object.create(defaults);

first.debug = true;
```

The corrected version keeps shared behavior on the prototype while storing instance-specific state directly on each object.

## 7. Best Practices

### Practice 1: Use prototypes for shared behavior, not shared data

Methods are ideal on prototypes because they can be reused by many objects. Mutable data is usually better stored on each object individually.

```javascript
const accountProto = {
  deposit(amount) {
    this.balance += amount;
  }
};

const account = Object.create(accountProto);
account.balance = 100;
```

This keeps behavior shared and state isolated.

### Practice 2: Prefer `Object.getPrototypeOf` and `Object.create` for clarity

These built-in methods make your intent obvious and avoid relying on legacy prototype access patterns.

```javascript
const base = { kind: "base" };
const child = Object.create(base);

console.log(Object.getPrototypeOf(child) === base);
```

Output (tested with Node 24.21.0):

```text
true
```

The code clearly expresses prototype relationships without needing low-level internals.

### Practice 3: Use `Object.hasOwn` for direct-property checks

When validating user input or configuration objects, direct-property checks are safer and easier to reason about.

```javascript
function readPort(config) {
  if (Object.hasOwn(config, "port")) {
    return config.port;
  }

  return 3000;
}
```

This avoids accidentally accepting inherited properties from an unexpected prototype.

## 8. Limitations and Edge Cases

- Primitive values such as strings and numbers are temporarily wrapped when you access methods, so their behavior can still appear prototype-based.
- The prototype chain ends at `null`; trying to look beyond that is impossible.
- `in` checks inherited properties, while `Object.hasOwn` does not.
- Changing an object’s prototype at runtime is possible but usually discouraged because it can make code slower and harder to predict.
- Some built-in methods come from intermediate prototypes, not just one obvious top-level prototype, so the lookup path can be longer than expected.
- Custom objects created with `Object.create(null)` have no prototype chain at all, so they do not inherit `toString()`, `hasOwnProperty()`, or other common methods.

## 9. Practical Mini Project

Let’s build a tiny task item system using a shared prototype. Each task keeps its own state, while all tasks share the same behavior.

```javascript
const taskPrototype = {
  toggle() {
    this.done = !this.done;
  },
  status() {
    return this.done ? "done" : "open";
  }
};

function createTask(title) {
  const task = Object.create(taskPrototype);
  task.title = title;
  task.done = false;
  return task;
}

const task1 = createTask("Write docs");
const task2 = createTask("Review PR");

task1.toggle();

console.log(task1.status()); // done
console.log(task2.status()); // open
```

Output (tested with Node 24.21.0):

```text
done
open
```

This project shows the main idea of prototype-based reuse: one shared set of methods, many independent objects.

## 10. Key Points

- The prototype chain is JavaScript’s property lookup path for objects.
- JavaScript checks the object first, then walks up through prototypes until it reaches `null`.
- Shared methods usually belong on prototypes, not on each object instance.
- `Object.hasOwn` helps you distinguish own properties from inherited ones.
- Built-in objects use the same mechanism, which is why familiar methods are available on arrays, strings, and many other values.

## 11. Practice Exercise

Try this small exercise to reinforce prototype lookup and property ownership.

- Create a base object with a `describe()` method that returns a short sentence.
- Create two child objects using `Object.create`.
- Give each child its own `name` property.
- Call `describe()` on both children.
- Check which properties are own properties and which are inherited.

**Expected output:** both objects should use the shared `describe()` method, while each object keeps its own name.

**Hint:** Put the method on the base object, then assign instance-specific properties after creating each child.

**Solution:**

```javascript
const base = {
  describe() {
    return `Name: ${this.name}`;
  }
};

const first = Object.create(base);
first.name = "Alpha";

const second = Object.create(base);
second.name = "Beta";

console.log(first.describe());
console.log(second.describe());
console.log(Object.hasOwn(first, "describe"));
console.log(Object.hasOwn(first, "name"));
```

Output (tested with Node 24.21.0):

```text
Name: Alpha
Name: Beta
false
true
```

## 12. Final Summary

The JavaScript prototype chain is how objects inherit behavior and how property lookup works behind the scenes. If a property is not found on an object, JavaScript continues searching through that object’s prototype chain until it either finds the property or reaches `null`.

Once you understand this lookup process, built-in objects become much easier to reason about, and your own object designs become clearer too. Use prototypes for shared methods, keep instance data on each object, and use `Object.hasOwn` when you need to tell direct properties apart from inherited ones.

Next, learn how constructor functions and classes build on the prototype chain so you can connect this concept to modern JavaScript object patterns.
