JavaScript Inheritance

Prototype-based Inheritance:

  • JavaScript is unique because it uses prototype-based inheritance. Unlike "classical" languages like Java or C++, JavaScript doesn't actually copy properties from one class to another. Instead, it links objects together. When you try to access a property on an object, JavaScript looks at that object first; if it can't find it there, it "asks" the object's prototype.
  • This mechanism allows for efficient memory usage because multiple objects can share a single copy of a method rather than each instance carrying its own copy.
Developer Tip: Even though modern JavaScript uses the class syntax, it is still just "syntactic sugar" over prototypes. Understanding prototypes is essential for debugging complex inheritance issues.

Prototype Chain:

  • Every JavaScript object has a hidden internal property called [[Prototype]]. In the browser, this is often exposed as __proto__. This reference points to another object, which points to another, and so on.
  • This sequence is known as the prototype chain. The chain continues until it reaches Object.prototype, and finally null, which signals the end of the line.
Watch Out: If you create a very deep prototype chain, property lookups can become slower because the engine has to traverse multiple levels to find a value. Keep your hierarchies flat and logical.

Example: Creating a Parent Object

In this example, we define a base "class" using a constructor function. We attach methods to the prototype so they aren't recreated every time we make a new shape.

function Shape(color) {
  // Instance property: unique to every shape
  this.color = color;
}

// Prototype method: shared by all shapes
Shape.prototype.getColor = function() {
  return this.color;
};
Best Practice: Always define methods on the prototype rather than inside the constructor function. This saves memory because all instances share the same function reference.

Creating a Child Object (Inheriting from Parent)

To inherit, we need to do two things: link the data (properties) and link the behavior (methods).

function Circle(radius, color) {
  // 1. "Borrow" the Parent constructor to set properties
  Shape.call(this, color); 
  
  this.radius = radius;
}

// 2. Link the prototypes so Circle can use Shape's methods
Circle.prototype = Object.create(Shape.prototype); 

// 3. Fix the constructor reference (otherwise it would point to Shape)
Circle.prototype.constructor = Circle; 

Circle.prototype.getRadius = function() {
  return this.radius;
};
Common Mistake: Forgetting to reset the constructor. If you omit Circle.prototype.constructor = Circle;, an instance of Circle will report its constructor as Shape, which can break logic that relies on identifying object types.

Instanceof Operator:

  • The instanceof operator is a way to check an object's "ancestry." It checks if a specific constructor's prototype appears anywhere in the object's prototype chain.
  • This is incredibly useful for writing functions that need to handle different types of objects differently (polymorphism).

Example: Using Instanceof

const circle = new Circle(5, 'red');

// True: it was created by the Circle constructor
console.log(circle instanceof Circle); 

// True: it inherits from Shape through the prototype chain
console.log(circle instanceof Shape); 

// True: everything (mostly) inherits from Object
console.log(circle instanceof Object); 
Developer Tip: In real-world apps, inheritance is great for UI components. You might have a base Button component and more specific SubmitButton or DeleteButton children that inherit the base styling and click logic.

 

Key Points

  • Delegation, not duplication: Objects delegate property lookups to their prototypes, allowing for powerful code reuse.
  • The Constructor Pattern: Use Parent.call(this, args) to initialize parent properties within a child object.
  • Object.create: This is the standard way to create a new object using an existing object as its prototype without executing the parent's constructor again.
  • The Chain: If a property isn't found on the object, JS searches up the chain until it finds it or hits null.