What is the prototype chain in JavaScript?

Intermediate15 min interview
Skills tested:
Prototype chain property lookup orderObject.create and explicit prototype assignmentclass syntax as sugar over prototype inheritancefor...in traversal including inherited propertiesDangers of mutating built-in prototypes

Advertisement

🧩 Scenario

In a real codebase, you will encounter the prototype chain when debugging why a method appears available on an object without being explicitly defined on it, when working with class inheritance, when reading JavaScript engine behavior for performance-sensitive code, and when using for...in loops that unexpectedly include inherited properties. Understanding the prototype chain also helps you understand why modifying built-in prototypes is dangerous and why class syntax does not introduce a fundamentally different inheritance model.

Architecture Walkthrough

Property Lookup Through the Chain

Every JavaScript object has an internal [[Prototype]] slot: a reference to another object or null. When you access a property on an object, the engine first checks the object's own properties. If the property is not found, it follows the [[Prototype]] link to the next object and checks there. This continues up the chain until a property is found or the chain reaches null, at which point the result is undefined.

Object.prototype sits at the top of the chain for all plain objects. It provides commonly available methods like toString, hasOwnProperty, and valueOf. Every object created with an object literal or Object.create inherits from Object.prototype unless explicitly set to inherit from null.

Object.create and Explicit Prototype Relationships

Object.create(proto) creates a new object whose [[Prototype]] is set to proto. This is the most direct way to set up an explicit inheritance relationship without using constructor functions or class syntax. The new object starts with no own properties; all methods are inherited from the prototype.

Object.getPrototypeOf(obj) is the standard way to read an object's prototype. Direct access via __proto__ works in most environments but is non-standard and should be avoided in production code. Object.hasOwn(obj, key) (or the older obj.hasOwnProperty(key)) checks whether a property exists directly on the object rather than somewhere in the chain.

class Syntax Is Syntactic Sugar

The class keyword introduced in ES6 does not create a new inheritance model. It is syntactic sugar over the existing prototype-based system. When you write class Dog extends Animal, JavaScript sets up Dog.prototype to have Animal.prototype as its [[Prototype]]. Instances of Dog follow the chain: instance → Dog.prototype → Animal.prototype → Object.prototype → null.

Every method defined in a class body is placed on the class's prototype object, not on each instance. This means all instances share the same method references, which is memory-efficient. Instance properties assigned in the constructor (using this.x = ... or class fields) are own properties on each instance.


Key Code Explained

// Explicit prototype chain with Object.create
const animal = {
  breathe() {
    return `${this.name} breathes`;
  },
};

const dog = Object.create(animal); // dog.[[Prototype]] === animal
dog.name = 'Rex';
dog.bark = function () {
  return 'Woof!';
};

console.log(dog.bark()); // 'Woof!' — own method
console.log(dog.breathe()); // 'Rex breathes' — found on animal via chain
console.log(dog.toString()); // found on Object.prototype

console.log(Object.getPrototypeOf(dog) === animal); // true
console.log(Object.getPrototypeOf(animal) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null

// class syntax: same chain, different spelling
class Animal {
  breathe() {
    return `${this.name} breathes`;
  }
}
class Dog extends Animal {
  bark() {
    return 'Woof!';
  }
}
const rex = new Dog();
rex.name = 'Rex';
// rex → Dog.prototype → Animal.prototype → Object.prototype → null
console.log(Object.getPrototypeOf(rex) === Dog.prototype); // true

// Built-in methods live on prototypes, not instances
const arr = [1, 2, 3];
console.log(arr.hasOwnProperty('map')); // false
console.log('map' in arr); // true — found via chain
console.log(Array.prototype.hasOwnProperty('map')); // true

// for...in traverses the entire chain — can include inherited properties
const parent = { inherited: true };
const child = Object.create(parent);
child.own = true;

for (const key in child) {
  console.log(key); // 'own', then 'inherited'
}

// Use Object.hasOwn to check only own properties
for (const key in child) {
  if (Object.hasOwn(child, key)) console.log(key); // 'own' only
}

The for...in behavior is the most practical gotcha: it enumerates all enumerable properties across the entire chain. Modern code prefers Object.keys() (own enumerable string-keyed properties), Object.entries(), or for...of with Object.entries() to avoid this.


Tradeoffs

ApproachProCon
class extendsReadable, familiar for OOP developers, tooling-friendlyHides prototype mechanics, makes engine behavior less obvious
Object.createExplicit prototype control, no constructor complexityVerbose for complex hierarchies, less common in modern code
Composition over inheritanceNo prototype chain complexity, flexibleRequires more deliberate design upfront

What Interviewers Actually Check

  • Whether you can trace property lookup order along the chain including what happens at the end
  • Whether you know class syntax is sugar over prototypes, not a different mechanism
  • Whether you know that built-in methods live on Array.prototype, not on each array instance
  • Whether you know the danger of modifying built-in prototypes
  • Whether you know for...in includes inherited properties and can name the alternative

Follow-Up Questions

  1. What is the difference between Object.create(null) and {} as a starting point for a plain data object?
  2. How does instanceof check the prototype chain, and what are its limitations?
  3. If you add a method to Dog.prototype after creating an instance, can the instance call that method?
  4. How does the prototype chain relate to memory usage when you have thousands of instances?
  5. What does it mean to "shadow" a prototype method, and when would you intentionally do it?

Common Candidate Mistakes

  • Using __proto__ to inspect or set the prototype instead of Object.getPrototypeOf and Object.setPrototypeOf
  • Not knowing that for...in includes inherited enumerable properties, leading to unexpected key enumeration
  • Believing class creates a fundamentally different inheritance model when it is purely syntactic sugar
  • Modifying Array.prototype or Object.prototype in application code and not understanding why it is dangerous
  • Not knowing the difference between own properties and inherited properties or how to check for each

Interview Readiness Checklist

Before you leave this question, make sure you can answer:

  • Can you describe property lookup order along the prototype chain ending at null?
  • Can you use Object.create to set up an explicit prototype relationship without class syntax?
  • Can you draw the prototype chain for a class that extends another class?
  • Can you explain why all arrays share .map without it being duplicated on each instance?
  • Can you describe what for...in includes and how to restrict enumeration to own properties?

Summary

Every JavaScript object has an internal [[Prototype]] link to another object or null. When a property is accessed, the engine walks this chain from the object outward until it finds the property or reaches null. This prototype chain is the mechanism behind all JavaScript inheritance.

The class keyword is syntactic sugar over this mechanism. Class methods are placed on the class's prototype, and extends wires SubClass.prototype to inherit from SuperClass.prototype. No new inheritance model is introduced; the prototype chain operates exactly as it does with Object.create.

The practical gotchas are for...in looping over inherited properties and the danger of modifying built-in prototypes like Array.prototype. Modern code uses Object.keys(), Object.hasOwn(), and for...of to work around these edge cases cleanly.

Related Videos
Watch these videos to learn more about this topic
Frequently Asked Questions

Do arrow functions have prototypes?

No, arrow functions do not have a prototype property and cannot be used as constructors.

Advertisement


Stay Updated

Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.

Advertisement