Overview

If we want to share properties among many objects of the same type, we can use the Prototype pattern.

classDiagram
    class Dog {
        +wagTail()
        +bark()
    }

    class dog1 {
        +name: "Max"
        +age: 4
    }

    class dog2 {
        +name: "Sam"
        +age: 2
    }

    class dog3 {
        +name: "Joy"
        +age: 6
    }

    class dog4 {
        +name: "Spot"
        +age: 8
    }

    Dog <|-- dog1 : __proto__
    Dog <|-- dog2 : __proto__
    Dog <|-- dog3 : __proto__
    Dog <|-- dog4 : __proto__

Implementation

Say we wanted to create many dogs with a createDog factory function.

const createDog = (name, age) => ({
  name,
  age,
  bark() {
    console.log(`${name} is barking!`)
  },
  wagTail() {
    console.log(`${name} is wagging their tail!`)
  },
})
 
const dog1 = createDog("Max", 4)
const dog2 = createDog("Sam", 2)
const dog3 = createDog("Joy", 6)
const dog4 = createDog("Spot", 8)

This way, we can easily create many dog objects with the same properties.

classDiagram
    class Dog {
        +wagTail()
        +bark()
    }

    class dog1 {
        +name: "Max"
        +age: 4
        +wagTail()
        +bark()
    }

    class dog2 {
        +name: "Sam"
        +age: 2
        +wagTail()
        +bark()
    }

    class dog3 {
        +name: "Joy"
        +age: 6
        +wagTail()
        +bark()
    }

    class dog4 {
        +name: "Spot"
        +age: 8
        +wagTail()
        +bark()
    }

However, we’re unnecessarily adding a new bark and wagTail methods to each dog object. Under the hood, we’re creating two new functions for each dog object, which uses memory.

We can use the Prototype Pattern to share these methods among many dog objects.

class Dog {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
 
  bark() {
    console.log(`${this.name} is barking!`)
  }
  wagTail() {
    console.log(`${this.name} is wagging their tail!`)
  }
}
 
const dog1 = new Dog("Max", 4)
const dog2 = new Dog("Sam", 2)
const dog3 = new Dog("Joy", 6)
const dog4 = new Dog("Spot", 8)

ES6 classes allow us to easily share properties among many instances, bark and wagTail in this case.

Tradeoffs

Memory efficient

The prototype chain allows us to access properties that aren’t directly defined on the object itself, we can avoid duplication of methods and properties, thus reducing the amount of memory used.

Readaibility

When a class has been extended many times, it can be difficult to know where certain properties come from.

For example, if we have a BorderCollie class that extends all the way to the Animal class, it can be difficult to trace back where certain properties came from.