The Prototype Pattern in JavaScript (ES6+)

The Prototype Pattern in JavaScript (ES6+)

The Prototype pattern is a creational design pattern that helps abstract the process of object creation. Instead of creating a new object manually and assigning all properties from another object, this pattern allows us to clone existing objects, preserving their properties and methods.

Understanding the Prototype Pattern

prototype is an object that serves as a blueprint for creating new objects with the same structure and functionality. This pattern eliminates the need for repetitive initialization logic by allowing objects to be created dynamically based on an existing instance.

Rather than using the new operator to instantiate an object, we create objects that follow the Prototype interface, which contains a single method: clone(). This method generates an exact copy of the original object, replicating all of its properties.

When to Use the Prototype Pattern?

Consider using the Prototype pattern when the following conditions apply:

  • You need to duplicate objects at runtime. If you already have objects and want to generate identical copies at runtime without going back to the factory method, this pattern is useful.
  • You want to avoid direct use of the new keyword. Object creation using new can be costly. The Prototype pattern provides an alternative approach, reducing overhead.
  • You need to copy objects with complex structures. When an object consists of nested or hierarchical structures, this pattern allows cloning of the entire object tree without manually recreating each sub-object.

UML Diagram of the Prototype Pattern

To understand the structure of the Prototype pattern, let’s break it down into three key components:

  1. Prototype Interface – Defines the clone() method.
  2. Concrete Prototype Classes – Implement the clone() method to return a copy of themselves.
  3. Client Code – Uses the Prototype interface to clone objects without directly referencing their classes.

In UML notation:

  • The Prototype interface defines the clone() method.
  • Concrete Prototypes implement the clone() method.
  • The Client references the Prototype interface rather than specific implementations, enabling flexible object duplication.


Implementing the Prototype Pattern in JavaScript (ES6+)

Following the UML structure, let’s implement the Prototype pattern with a practical example.

Example: Cloning Game Characters

Imagine we are building a video game where different types of characters need to be created dynamically. Instead of manually creating each character and assigning values, we can use the Prototype pattern to clone existing ones.

Step 1: Defining the Prototype Interface

First, we create an interface that defines the clone() method:

class CharacterPrototype {
  clone() {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
}        

This method uses Object.assign() to copy the properties of an object while maintaining its prototype chain.


Step 2: Implementing Concrete Classes

Next, we create two concrete classes: Warrior and Mage, which extend CharacterPrototype:

class Warrior extends CharacterPrototype {
  constructor(name, level, weapon) {
    super();
    this.name = name;
    this.level = level;
    this.weapon = weapon;
  }

  attack() {
    return `${this.name} attacks with ${this.weapon}!`;
  }
}

class Mage extends CharacterPrototype {
  constructor(name, level, spell) {
    super();
    this.name = name;
    this.level = level;
    this.spell = spell;
  }

  castSpell() {
    return `${this.name} casts ${this.spell}!`;
  }
}        

Here, Warrior and Mage extend CharacterPrototype, ensuring that they both have the clone() method.


Step 3: Using the Prototype Pattern

Now, let’s create instances and clone them at runtime:

const warrior1 = new Warrior("Thor", 10, "Thunder Sword");
const warriorClone = warrior1.clone();

console.log(warriorClone); 
// Warrior { name: 'Thor', level: 10, weapon: 'Thunder Sword' }

const mage1 = new Mage("Merlin", 15, "Fireball");
const mageClone = mage1.clone();

console.log(mageClone); 
// Mage { name: 'Merlin', level: 15, spell: 'Fireball' }        

As seen in the output, clone() creates a new object with the same properties as the original but as a separate instance.


Handling Deep Cloning (Nested Objects)

The previous example performs a shallow copy, meaning that nested objects inside our character instances would still be referenced rather than duplicated. To avoid this, we can implement deep cloning.

Step 4: Implementing Deep Clone

A simple way to perform deep cloning in JavaScript is by using JSON.parse(JSON.stringify(obj)). While this approach works for many cases, it has limitations (e.g., it does not preserve functions or circular references).

function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}        

Now, we modify the clone() method to use deep cloning:

class CharacterPrototype {
  clone() {
    return deepClone(this);
  }
}        

With this update, objects with nested properties are fully duplicated without retaining references to the original.


Conclusion

The Prototype pattern is a powerful tool for managing object duplication in JavaScript. It helps avoid excessive use of new, improves performance, and simplifies code by allowing efficient cloning of complex structures.

When Should You Use the Prototype Pattern?

✔ When you need efficient object duplication.

✔ When direct instantiation with new is too expensive.

✔ When working with nested or hierarchical object structures.

By leveraging Object.assign(), Object.create(), and deep cloning techniques, you can implement the Prototype pattern effectively in ES6+.

This approach is widely used in game development, UI frameworks, and any system that requires dynamic object creation. 

The Prototype Pattern is a game-changer for optimizing object creation! Love how it reduces redundancy and improves efficiency. Great insights!

To view or add a comment, sign in

More articles by Oleksii Savchenko

Others also viewed

Explore content categories