JavaScript Prototypal Inheritance

Understand the prototype chain, Object.create, and how class syntax wraps prototypal inheritance.

TL;DR

  1. 01Inherit object properties dynamically through a linked chain of prototypes.
  2. 02Construct prototype linkages directly using the standard Object.create method.
  3. 03Use modern class declarations as syntactic sugar over prototype chains.

Tips

  1. 01Use the standard Object.getPrototypeOf() method instead of legacy properties like __proto__ to inspect prototypes.
  2. 02Verify own object properties explicitly using Object.hasOwn() before accessing inherited properties.

Warnings

  1. 01Avoid setting object prototypes dynamically using Object.setPrototypeOf because it degrades property access performance.
  2. 02Filter properties inside for...in loops to prevent iteration leaks from inherited enumerable method names.

The Prototype Chain

    Prototype linkage

    Links objects together in a prototype chain for sharing property values.

    const animal = { eats: true };
    const rabbit = Object.create(animal);
    rabbit.hops = true;
    console.log(rabbit.eats); // true
    Chain lookup rules

    Walks up prototype chains until matching properties are found.

    // Lookup: rabbit -> animal -> Object.prototype
    Chain end

    Ends prototype search trees at Object.prototype, which has null prototype.

    const proto = Object.getPrototypeOf(
      Object.prototype
    );
    console.log(proto); // null

Creating Prototypes

    Object.create

    Creates new object instances with explicit prototype mappings.

    const base = { greet() { return "hi"; } };
    const obj = Object.create(base);
    console.log(obj.greet()); // "hi"
    Inspecting prototypes

    Inspects prototype references using getPrototypeOf cleanly.

    Object.getPrototypeOf(obj) === base; // true
    Inheritance without classes

    Models inheritance associations directly without requiring constructors.

    const parent = { val: 42 };
    const child = Object.create(parent);

Constructor prototype

    Constructor functions

    Attaches shared functions directly to constructor prototype properties.

    function Dog(name) { this.name = name; }
    Dog.prototype.bark = function() {
      return `${this.name} barks`;
    };
    const rex = new Dog("Rex");
    Memory optimization

    Shares method references across all constructed instances.

    // rex.bark links to Dog.prototype.bark
    instanceof verification

    Confirms prototype links exist in target instance chains.

    console.log(rex instanceof Dog); // true

Class Sugar

    class keyword

    Compiles class helper blocks to standard prototypes.

    class Dog {
      constructor(name) { this.name = name; }
      bark() { return `${this.name} barks`; }
    }
    typeof Dog; // "function"
    Subclass extends

    Sets up prototype chains between parent and child automatically.

    class Puppy extends Dog {
      bark() { return super.bark() + "!"; }
    }

Property Shadowing

    hasOwn check

    Checks property existence directly on the local object instance.

    const base = { color: "red" };
    const item = Object.create(base);
    console.log(Object.hasOwn(item, "color")); // false
    Property overrides

    Shadows prototype property definitions by setting local values.

    item.color = "blue";
    console.log(item.color); // "blue"
    console.log(base.color); // "red"

In Practice

FAQ