Tutorial

Understanding Prototypes and Inheritance in JavaScript

Clear, original guide explaining JavaScript prototype inheritance, the prototype chain, constructor functions, and practical examples.

Drake Nguyen

Founder · System Architect

3 min read
Understanding Prototypes and Inheritance in JavaScript
Understanding Prototypes and Inheritance in JavaScript

Introduction

JavaScript is a prototype-based language where objects inherit behavior by linking to other objects rather than by instantiating classes. This article explains JavaScript prototype inheritance and the prototype chain, shows how constructor function JavaScript patterns work, and gives practical examples for adding methods to prototypes and setting up inheritance.

JavaScript prototypes and the internal [[Prototype]]

Every object in JavaScript has an internal slot often referred to as [[Prototype]] (exposed in some engines as __proto__). When you try to read a property, the runtime looks first on the object itself and then follows the prototype chain until it finds the property or reaches the end at Object.prototype.

Inspecting an object's prototype

const a = {};
// preferred: read the internal prototype
Object.getPrototypeOf(a); // typically returns Object.prototype

// legacy, not recommended for production
a.__proto__;
Note: __proto__ is a legacy accessor. Use Object.getPrototypeOf for reliable behavior across environments.

Prototype chain and property lookup

The prototype chain is the sequence of objects followed during property lookup. For example, an array instance delegates to Array.prototype, which itself delegates to Object.prototype. That path is what we mean by the prototype chain.

  • Property lookup begins on the instance and walks the prototype chain.
  • The chain ends when the lookup reaches Object.prototype, whose prototype is null.
  • Operators and helpers: instanceof checks if a constructor's prototype exists in an object's prototype chain; isPrototypeOf is another way to test the relationship.

Examples: Array chain and instanceof

const arr = [];
Object.getPrototypeOf(arr) === Array.prototype; // true
Object.getPrototypeOf(Array.prototype) === Object.prototype; // true

arr instanceof Array; // true
Array.prototype.isPrototypeOf(arr); // true

Constructor functions and adding methods to prototypes

Before ES6 classes, constructor function JavaScript patterns were a common way to create object templates. A constructor is any function used with new. Methods shared by instances are typically attached to the constructor's prototype so they are stored once and reused by all instances.

Constructor example

// Define a constructor
function Player(name, lvl) {
  this.name = name;
  this.level = lvl;
}

// Add a method to the prototype for reuse
Player.prototype.introduce = function() {
  return `${this.name} (level ${this.level})`;
};

const p = new Player('Asha', 3);
p.introduce(); // "Asha (level 3)"

Setting up inheritance with constructor functions

To share prototype methods between constructors, you link prototypes. One approach is to set the child constructor's prototype to inherit from the parent constructor's prototype. Avoid copying instance properties with call alone because that does not establish a prototype relationship.

Child constructors and linking prototypes

function Knight(name, lvl, weapon) {
  Player.call(this, name, lvl); // borrow Player instance properties
  this.weapon = weapon;
}

// Link prototypes so Knight instances delegate to Player.prototype
Object.setPrototypeOf(Knight.prototype, Player.prototype);

Knight.prototype.attack = function() {
  return `${this.name} attacks with ${this.weapon}`;
};

const k = new Knight('Bran', 2, 'sword');
// Methods from both Knight.prototype and Player.prototype are available
k.attack();      // "Bran attacks with sword"
k.introduce();   // "Bran (level 2)"

Alternative: create the child prototype from the parent prototype using Object.create to avoid modifying existing prototype objects.

Using Object.create for safer prototype inheritance

Knight.prototype = Object.create(Player.prototype);
Knight.prototype.constructor = Knight;

Common questions and differences

  • Difference between __proto__ and prototype: prototype is a property on functions used when creating instances; __proto__ (or the internal [[Prototype]]) is the reference an object uses to delegate.
  • Object.getPrototypeOf vs __proto__: prefer Object.getPrototypeOf for standards-compliant reads; use Object.setPrototypeOf or Object.create to change/link prototypes if necessary.
  • prototype chain ends at Object.prototype: attempts to go past that point return null.

Best practices for JavaScript prototype inheritance

  • Prefer composition or ES6 classes for clear intent in new code, but understanding prototypal inheritance remains essential for legacy and deeper JavaScript knowledge.
  • Attach shared methods to prototypes (e.g., how to add methods to a constructor function prototype) rather than recreating them per instance.
  • Use Object.getPrototypeOf, Object.setPrototypeOf, and Object.create carefully—changing prototype chains at runtime can hurt performance.

Conclusion

JavaScript prototype inheritance is a powerful delegation model that underpins objects and inheritance in the language. By learning how the prototype chain, constructor function JavaScript patterns, and prototype utilities (Object.getPrototypeOf, Object.setPrototypeOf, Object.create) work, you can design efficient and maintainable object relationships and avoid common pitfalls.

Stay updated with Netalith

Get coding resources, product updates, and special offers directly in your inbox.