Prototypes and Prototype Inheritance

See how objects share methods through prototypes, follow the prototype chain, use Object.create, and understand what classes do behind the scenes.

Lesson 53 of 56Advanced16 min read

Here's a small mystery. You create an empty array, const list = [], and you can immediately call list.push(), list.map() and list.includes(). But you never added those methods to list. Where do they come from? The answer is the prototype.

Every JavaScript object has a hidden link to another object called its prototype. When you read a property that the object doesn't have, JavaScript follows the link and looks on the prototype, then on the prototype's prototype, and so on. This series of links is the prototype chain. All arrays share one prototype, Array.prototype, which holds push, map and the rest, so a million arrays don't each need their own copy of every method.

This system is called prototypal inheritance, and it's how JavaScript does object-oriented programming. Even classes are built on top of it. In this lesson you'll inspect prototypes, follow the chain, create objects with Object.create, write the constructor functions older code uses, and see exactly what class and extends do behind the scenes.

The prototype chain

JavaScript
const animal = { breathes: true };
const dog = Object.create(animal); // dog's prototype is animal
dog.barks = true;

dog.barks;     // true: found on dog itself
dog.breathes;  // true: found on the prototype
dog.flies;     // undefined: not found anywhere in the chain

When you read dog.breathes, JavaScript checks dog, doesn't find it, then checks animal and finds it. The chain ends at Object.prototype, whose prototype is null. That's where methods every object has, such as toString and hasOwnProperty, live.

Writing is different from reading: dog.breathes = false creates a new property on dog itself and leaves animal untouched.

Inspecting prototypes

CodeGives
Object.getPrototypeOf(obj)The object's prototype (the modern way)
obj.__proto__The same, an older accessor you'll see in code and DevTools
Object.setPrototypeOf(obj, proto)Changes the prototype (slow, rarely needed)
Object.hasOwn(obj, "key")true only if the property is on the object itself
proto.isPrototypeOf(obj)Is proto anywhere in obj's chain?
obj instanceof FnIs Fn.prototype in obj's chain?

In DevTools, expand any object and look for the Prototype entry to walk up its chain.

Constructor functions

Before class existed, objects were created with constructor functions and new:

JavaScript
function Pet(name) {
  this.name = name;            // own property, per object
}
Pet.prototype.speak = function () {
  return `${this.name} makes a sound`;   // shared by all pets
};
const rex = new Pet("Rex");

new creates an empty object, sets its prototype to Pet.prototype, runs the function with this set to the new object, and returns it. Methods go on Pet.prototype so every pet shares one copy, while data such as name lives on each object.

Classes are prototypes underneath

JavaScript
class Pet {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
}
class Dog extends Pet {
  speak() { return `${this.name} barks`; }
}

This does the same as the constructor version: speak is stored on Pet.prototype, and extends sets Dog.prototype's prototype to Pet.prototype. So a dog looks up methods on Dog.prototype first, then Pet.prototype, then Object.prototype. Classes are clearer to write, so use them in new code, but knowing the prototype underneath explains how instanceof, method overriding and super really work.

JavaScript Prototypes Syntax

Prototypes

const child = Object.create(parent);
Object.getPrototypeOf(child) === parent; // true
Object.hasOwn(child, "x");

function Car(make) { this.make = make; }
Car.prototype.drive = function () { return `${this.make} drives`; };
const c = new Car("Mini");
c instanceof Car; // true

JavaScript Prototypes Summary

  • Every object has a prototype; missing properties are looked up along the prototype chain.
  • Built-in methods like push and toUpperCase live on shared prototypes.
  • Object.create(proto) makes an object with a chosen prototype.
  • Constructor functions put shared methods on Fn.prototype; new links new objects to it.
  • class and extends are a clearer syntax built on prototypes.

JavaScript Prototypes Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

Following the prototype chain

const list = [3, 1, 2];

console.log(Object.hasOwn(list, "map"));
console.log(Object.getPrototypeOf(list) === Array.prototype);
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));

const base = { greet() { return `Hi, I'm ${this.name}`; } };
const ana = Object.create(base);
ana.name = "Ana";
console.log(ana.greet());
console.log("Own keys:", Object.keys(ana));
console.log(base.isPrototypeOf(ana));
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 3map isn't on the array itself.
  2. Line 4It comes from Array.prototype, the array's prototype.
  3. Lines 5 to 6The chain continues to Object.prototype and ends at null.
  4. Lines 8 to 10ana gets greet from its prototype, while name is her own.
  5. Line 11Only own properties are listed by Object.keys.
Example 2

Constructor functions and shared methods

function Counter(start) {
  this.value = start;
}
Counter.prototype.increment = function () {
  this.value++;
  return this.value;
};

const a = new Counter(0);
const b = new Counter(10);
a.increment();
console.log(a.value, b.increment());

console.log("Shared method:", a.increment === b.increment);
console.log("Own data:", Object.hasOwn(a, "value"), Object.hasOwn(a, "increment"));
console.log(a instanceof Counter, Object.getPrototypeOf(a) === Counter.prototype);
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 2Each counter gets its own value.
  2. Lines 4 to 7increment is stored once on the prototype.
  3. Line 14Both counters use the very same function.
  4. Line 15value is own data; increment is inherited.
  5. Line 16instanceof checks the prototype chain.
Example 3

What class and extends really build

class Shape {
  constructor(name) { this.name = name; }
  describe() { return `${this.name} with area ${this.area().toFixed(1)}`; }
}

class Circle extends Shape {
  constructor(r) { super("Circle"); this.r = r; }
  area() { return Math.PI * this.r ** 2; }
}

const c = new Circle(2);
console.log(c.describe());
console.log(typeof Shape);
console.log(Object.getPrototypeOf(Circle.prototype) === Shape.prototype);
console.log(Object.hasOwn(Circle.prototype, "area"), Object.hasOwn(Shape.prototype, "describe"));
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 3describe is stored on Shape.prototype.
  2. Line 8area is stored on Circle.prototype.
  3. Line 12describe is found by walking up from Circle.prototype to Shape.prototype.
  4. Line 13A class is really a special kind of function.
  5. Line 14extends links the two prototypes together.

Common JavaScript Prototypes Mistakes

Methods inside the constructor

Don't
function User(name) {
  this.name = name;
  this.hello = function () { return "Hi " + this.name; }; // a new copy for every user
}
Do
function User(name) { this.name = name; }
User.prototype.hello = function () { return "Hi " + this.name; };

Forgetting new

Don't
const u = User("Sam"); // this is not a new object; u is undefined
Do
const u = new User("Sam");

Classes throw an error if you forget new, which is another reason to prefer them.

Using arrow functions on prototypes

Don't
User.prototype.hello = () => "Hi " + this.name; // this is not the user
Do
User.prototype.hello = function () { return "Hi " + this.name; };

JavaScript Prototypes in Real Projects

Prototypes in practice

Every built-in type, from arrays and strings to DOM elements, gets its methods from prototypes: a <button> element's chain runs through HTMLButtonElement, HTMLElement, Element and Node. Older libraries such as jQuery were built with constructor functions and prototypes. Polyfills add missing methods to prototypes in old browsers, and interview questions about this, new and inheritance all come back to the prototype chain.

JavaScript Prototypes Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Create cat with animal as its prototype.

const animal = { eats: true };
const cat = Object.(animal);
console.log(cat.eats);
Predict the outputmedium

What is logged?

const base = { colour: "red" };
const item = Object.create(base);
item.colour = "blue";
console.log(item.colour, base.colour, Object.keys(item).length);
Multiple choiceeasy

Where do array methods like map come from?

Multiple choicemedium

What does new do first when calling a constructor function?

Fix the bugmedium

Every Robot should share one greet method from the prototype, and greet should use the robot's own name. Fix it.

function Robot(name) {
  this.name = name;
}
Robot.greet = () => `Beep, I am ${this.name}`;
const r1 = new Robot("R1");
const r2 = new Robot("R2");
console.log(r1.greet?.());
ConsoleEdit the code, then press Run or Ctrl+Enter
Checks
  • Robots can greet
  • The method is shared
  • The method lives on the prototype

JavaScript Prototypes Quiz

5 questions to check what you learned.

  1. What is at the very end of a prototype chain?

    • null
    • window
    • Array.prototype
    • undefined
  2. What does Object.getPrototypeOf(obj) return?

    • A copy of obj
    • The object obj inherits from
    • obj's constructor function
    • The keys of obj
  3. What does class Dog extends Animal set up?

    • Dog.prototype's prototype is Animal.prototype
    • A copy of every Animal method on Dog
    • A global Animal variable
    • Nothing until super is called
  4. Why put methods on the prototype rather than in the constructor?

    • They run faster once
    • Constructors can't contain functions
    • It's required by new
    • All objects share one copy instead of each getting its own
  5. What does obj instanceof Fn actually check?

    • Whether obj was created on the same line
    • Whether obj has the same keys as Fn
    • Whether Fn.prototype appears anywhere in obj's prototype chain
    • Whether typeof obj is "Fn"

JavaScript Prototypes Challenge

Build an inheritance chain without class

Using Object.create:

  1. Create vehicle with a method describe() returning `${this.name} has ${this.wheels} wheels`
  2. Create bike with vehicle as its prototype and wheels: 2
  3. Create myBike with bike as its prototype and name: "Roadster"
  4. myBike.describe() should return "Roadster has 2 wheels", and only name should be an own property of myBike
ConsoleEdit the code, then press Run or Ctrl+Enter
Checks
  • describe works through the chain
  • The chain is myBike, bike, vehicle
  • Only name is own