Tutorials Logic, IN info@tutorialslogic.com

JavaScript Prototypes Prototype Chain

JavaScript Prototype Chain

Property lookup follows JavaScript prototype chains until a property is found or the chain ends. Constructors and classes organize that model, while receiver, own-property, descriptor, and mutation rules remain fundamental.

Avoid global built-in patches, block prototype-pollution paths, inspect descriptors safely, and use composition when inheritance would couple unrelated behavior.

JavaScript Prototypes

JavaScript is a prototype-based language. This means objects can inherit properties and methods from other objects. Instead of copying every method into every object, JavaScript lets objects share behavior through a prototype chain.

Modern JavaScript has class syntax, but classes still use prototypes internally. Understanding prototypes helps you understand objects, inheritance, methods, this, new, class, and many interview questions much more clearly.

The Core Idea

When you access a property on an object, JavaScript first checks the object itself. If the property is not found, JavaScript looks at the object's prototype. If it is not found there, JavaScript continues up the prototype chain until it finds the property or reaches null.

The object user does not define toString, but it can still access it because ordinary objects inherit from Object.prototype.

Property Lookup

Property Lookup
const user = {
  name: 'Asha'
};

console.log(user.name);     // Asha - found on user
console.log(user.toString); // found on Object.prototype

// JavaScript looks like this:
// user -> Object.prototype -> null

Prototype vs prototype Property

This topic has confusing names, so separate these two ideas:

  • An object's internal prototype is the object it inherits from. It is often written as [[Prototype]].
  • A function's prototype property is used when that function is called with new.
  • The old __proto__ property points to an object's internal prototype, but prefer Object.getPrototypeOf().

Prototype Names

Prototype Names
function Student(name) {
  this.name = name;
}

const student = new Student('Riya');

console.log(Student.prototype); // object used for Student instances
console.log(Object.getPrototypeOf(student) === Student.prototype); // true

Constructor Functions and Prototypes

Before ES6 classes, constructor functions were the common way to create many similar objects. A constructor function is called with new. Methods should usually be placed on the constructor's prototype so all instances can share one method instead of creating a new function for every instance.

The two objects have different data, but they share the same introduce() method through Student.prototype.

Constructor Function

Constructor Function
function Student(name, course) {
  this.name = name;
  this.course = course;
}

Student.prototype.introduce = function() {
  return `${this.name} is learning ${this.course}.`;
};

const student1 = new Student('Aman', 'JavaScript');
const student2 = new Student('Neha', 'React');

console.log(student1.introduce()); // Aman is learning JavaScript.
console.log(student2.introduce()); // Neha is learning React.
console.log(student1.introduce === student2.introduce); // true

Instance Properties vs Prototype Properties

Properties placed directly on an instance belong only to that object. Properties placed on the prototype are shared through inheritance.

Instance vs Prototype

Instance vs Prototype
function Student(name) {
  this.name = name; // own property
}

Student.prototype.school = 'Tutorials Logic'; // shared prototype property

const student1 = new Student('Rohit');
const student2 = new Student('Kavya');

student1.age = 22; // only student1 has age

console.log(student1.name);   // Rohit
console.log(student1.school); // Tutorials Logic
console.log(student2.school); // Tutorials Logic
console.log(student2.age);    // undefined

The Prototype Chain

A prototype can also have its own prototype. This creates a chain. JavaScript walks this chain during property lookup.

Here, dog inherits from Dog.prototype, and Dog.prototype inherits from Animal.prototype. If JavaScript cannot find speak() on the dog itself or on Dog.prototype, it finds it on Animal.prototype.

Prototype Chain

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

Animal.prototype.speak = function() {
  return `${this.name} makes a sound.`;
};

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

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  return `${this.name} barks.`;
};

const dog = new Dog('Rex', 'Labrador');

console.log(dog.bark());  // Rex barks.
console.log(dog.speak()); // Rex makes a sound.

console.log(dog instanceof Dog);    // true
console.log(dog instanceof Animal); // true

Inspecting Prototypes

Use Object.getPrototypeOf() to inspect an object's prototype. Use Object.setPrototypeOf() rarely, because changing prototypes after objects are created can hurt performance and make code harder to reason about.

in checks the object and its prototype chain. Object.hasOwn() checks only the object's own properties.

Inspect Prototype Chain

Inspect Prototype Chain
const user = { name: 'Meera' };

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

console.log('toString' in user); // true, inherited
console.log(Object.hasOwn(user, 'toString')); // false
console.log(Object.hasOwn(user, 'name'));     // true

Object.create()

Object.create() creates a new object with a specific prototype. It is a direct way to build prototype inheritance without using a constructor function.

Object.create() - JavaScript Example

Object.create() - JavaScript Example
const userMethods = {
  greet() {
    return `Hello, ${this.name}`;
  }
};

const user = Object.create(userMethods);
user.name = 'Anaya';

console.log(user.greet()); // Hello, Anaya
console.log(Object.getPrototypeOf(user) === userMethods); // true

Objects with No Prototype

Sometimes you may want a plain dictionary object with no inherited properties. Object.create(null) creates an object whose prototype is null. This can be useful for maps or lookup tables, though Map is often better in modern JavaScript.

No Prototype Object

No Prototype Object
const dictionary = Object.create(null);

dictionary.javascript = 'A programming language';
dictionary.prototype = 'An inheritance object';

console.log(dictionary.javascript);
console.log(dictionary.toString); // undefined
console.log(Object.getPrototypeOf(dictionary)); // null

Classes Use Prototypes Internally

The class syntax is the modern way to write constructor-style object code. It is easier to read, but instance methods are still stored on the class prototype.

The title property belongs to the instance. The getTitle() method belongs to Course.prototype and is shared by all Course instances.

Class Prototype

Class Prototype
class Course {
  constructor(title) {
    this.title = title;
  }

  getTitle() {
    return this.title;
  }
}

const jsCourse = new Course('JavaScript');

console.log(jsCourse.getTitle()); // JavaScript
console.log(Object.getPrototypeOf(jsCourse) === Course.prototype); // true
console.log(Course.prototype.getTitle === jsCourse.getTitle);      // true

Class Inheritance and Prototypes

When a class extends another class, JavaScript sets up a prototype chain between the child prototype and the parent prototype.

Class Inheritance

Class Inheritance
class Vehicle {
  constructor(make) {
    this.make = make;
  }

  describe() {
    return `Vehicle: ${this.make}`;
  }
}

class Car extends Vehicle {
  constructor(make, model) {
    super(make);
    this.model = model;
  }

  details() {
    return `${this.describe()} ${this.model}`;
  }
}

const car = new Car('Toyota', 'Camry');

console.log(car.details()); // Vehicle: Toyota Camry
console.log(Object.getPrototypeOf(Car.prototype) === Vehicle.prototype); // true

Static Methods and Constructor Inheritance

Classes also have a prototype relationship at the constructor level. This is why static methods can be inherited by child classes.

Instance methods are found through Formatter.prototype. Static methods are found through the constructor function itself.

Static Methods

Static Methods
class Tool {
  static category() {
    return 'Developer Tool';
  }
}

class Formatter extends Tool {}

console.log(Formatter.category()); // Developer Tool
console.log(Object.getPrototypeOf(Formatter) === Tool); // true

Shadowing Prototype Properties

If an object has its own property with the same name as a prototype property, the own property wins. This is called shadowing.

Property Shadowing

Property Shadowing
const defaults = {
  theme: 'light'
};

const settings = Object.create(defaults);

console.log(settings.theme); // light - inherited

settings.theme = 'dark';

console.log(settings.theme); // dark - own property shadows prototype
console.log(Object.hasOwn(settings, 'theme')); // true

Do Not Put Mutable Data on Prototypes

Prototype properties are shared. That is good for methods, but dangerous for mutable data such as arrays and objects. Put instance-specific data inside the constructor instead.

Shared Mutable Pitfall

Shared Mutable Pitfall
function TodoList() {}

// Bad: every instance shares the same array.
TodoList.prototype.items = [];

const personal = new TodoList();
const work = new TodoList();

personal.items.push('Buy milk');

console.log(work.items); // ['Buy milk'] - unexpected

// Better:
function SafeTodoList() {
  this.items = [];
}

Built-in Prototypes

Arrays, strings, dates, functions, and regular expressions all inherit methods from their built-in prototypes. For example, arrays get methods from Array.prototype.

Built-in Prototypes - JavaScript Example

Built-in Prototypes - JavaScript Example
const numbers = [1, 2, 3];

console.log(numbers.map(n => n * 2)); // [2, 4, 6]
console.log(Object.getPrototypeOf(numbers) === Array.prototype); // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype); // true

Avoid Modifying Built-in Prototypes

JavaScript allows you to add methods to built-in prototypes, but this is risky in production code. It can conflict with future JavaScript features, third-party libraries, or other team code.

Avoid This Pattern

Avoid This Pattern
// Possible, but usually not recommended.
Array.prototype.sum = function() {
  return this.reduce((total, number) => total + number, 0);
};

console.log([1, 2, 3].sum()); // 6

// Safer alternative:
function sum(numbers) {
  return numbers.reduce((total, number) => total + number, 0);
}

Prototype Pollution

Prototype pollution is a security problem where unsafe code lets user-controlled input modify shared prototypes such as Object.prototype. This can affect many objects in the application. Be careful when merging untrusted objects, especially if keys like __proto__, constructor, or prototype are allowed.

In real applications, use well-maintained libraries and keep dependencies updated. Avoid writing deep merge logic for untrusted data unless you fully understand the security risks.

Unsafe Merge Example

Unsafe Merge Example
function safeAssign(target, source) {
  for (const key of Object.keys(source)) {
    if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
      continue;
    }

    target[key] = source[key];
  }

  return target;
}

const settings = safeAssign({}, JSON.parse('{"theme":"dark"}'));
console.log(settings.theme); // dark

Prototype Methods vs Arrow Functions

Prototype methods usually need dynamic this, so regular functions are normally the right choice. Arrow functions do not have their own this, which can cause confusing bugs when used as prototype methods.

Use Regular Functions

Use Regular Functions
function User(name) {
  this.name = name;
}

// Good: regular function gets this from the call site.
User.prototype.greet = function() {
  return `Hello, ${this.name}`;
};

const user = new User('Dev');
console.log(user.greet()); // Hello, Dev

A Practical Prototype Example

The example below creates reusable course objects. Instance properties store each course's data, while prototype methods define shared behavior.

Course Constructor

Course Constructor
function Course(title, lessons) {
  this.title = title;
  this.lessons = lessons;
  this.completed = 0;
}

Course.prototype.completeLesson = function() {
  if (this.completed < this.lessons) {
    this.completed += 1;
  }
};

Course.prototype.getProgress = function() {
  return `${this.completed}/${this.lessons} lessons completed`;
};

const js = new Course('JavaScript Prototypes', 12);
js.completeLesson();
js.completeLesson();

console.log(js.getProgress()); // 2/12 lessons completed

Interview Explanation

If someone asks "What is a prototype in JavaScript?", answer with the object lookup idea first. Then mention constructor functions and classes.

A strong answer: "A prototype is an object that another object can inherit from. When JavaScript cannot find a property directly on an object, it searches that object's prototype chain. Constructor functions and classes use prototypes to share methods across instances."

Interview Answer

Interview Answer
function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function() {
  return `Hi, I am ${this.name}`;
};

const person = new Person('Asha');

console.log(person.sayHi()); // Hi, I am Asha
console.log(Object.getPrototypeOf(person) === Person.prototype); // true

Prototype Summary Table

Term Meaning
[[Prototype]] The internal link from one object to another object it inherits from.
Function.prototype The object assigned as the prototype of instances created with new.
Object.getPrototypeOf(obj) Returns the prototype of an object.
Object.create(proto) Creates a new object with proto as its prototype.
instanceof Checks whether a constructor's prototype appears in an object's prototype chain.
Object.hasOwn(obj, key) Checks whether a property belongs directly to the object.

Common Beginner Mistakes

  • Confusing a function's prototype property with an object's internal [[Prototype]].
  • Putting arrays or objects on a prototype and accidentally sharing mutable data.
  • Using arrow functions for prototype methods that need this.
  • Modifying built-in prototypes such as Array.prototype in production code.
  • Forgetting to reset constructor after replacing a prototype manually.
  • Assuming class removes prototypes. Classes still use prototypes internally.

Summary

Prototypes are the foundation of JavaScript inheritance. Every normal object can inherit from another object through its prototype. Constructor functions and classes use prototypes to share methods efficiently across instances. Once you understand property lookup, prototype chains, Object.create(), and how classes map to prototypes, JavaScript's object model becomes much easier to reason about.

Prototype Safety and Diagnostics

Avoid modifying built-in prototypes in application code because changes affect unrelated consumers and can collide with future platform methods. Prefer local helpers, subclasses where substitution is valid, or composition. Cross-realm values may have different intrinsic prototypes, so prototype identity checks are not universal type validation.

Prototype pollution occurs when untrusted keys alter a shared prototype through unsafe merge or path-setting behavior. Reject dangerous keys, copy only schema-approved fields, prefer `Object.hasOwn` for ownership checks, and use null-prototype dictionaries where inherited properties are unwanted.

Debug lookup with own property descriptors, `Object.getPrototypeOf`, and a bounded walk of the chain. Getters and proxies can execute during ordinary inspection, so use descriptors when side effects matter. Keep object shapes predictable and measure before changing prototype strategy for performance.

  • Do not patch built-in prototypes globally.
  • Block untrusted prototype and constructor path keys.
  • Use own-property checks for external records.
  • Inspect descriptors and prototype links without triggering getters.
  • Test inherited-name collisions in dictionary-like objects.
Before you move on

JavaScript Prototypes Prototype Chain Mastery Check

5 checks
  • Distinguish own properties from inherited properties.
  • Keep constructor prototype links intentional.
  • Avoid mutating built-in prototypes.
  • Reject dangerous keys during external data merges.
  • Inspect descriptors and chains when diagnosing lookup.

JavaScript Questions Learners Ask

A prototype is an object that another object inherits from. If a property is not found directly on an object, JavaScript searches the prototype chain.

Classes are modern syntax for creating constructor-style objects, but they still use prototypes internally. Instance methods declared in a class are placed on the class prototype.

<code>prototype</code> is a property on functions used when creating instances with <code>new</code>. <code>__proto__</code> is an older accessor for an object's internal prototype. Prefer <code>Object.getPrototypeOf()</code> instead of <code>__proto__</code>.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.