Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying JavaScript Design Patterns
  • Table Of Contents Toc
  • Feedback & Rating feedback
JavaScript Design Patterns

JavaScript Design Patterns

By : Hugo Di Francesco
5 (5)
close
close
JavaScript Design Patterns

JavaScript Design Patterns

5 (5)
By: Hugo Di Francesco

Overview of this book

Unlock the potential of JavaScript design patterns, the foundation for development teams seeking structured and reusable solutions to common software development challenges in this guide to improving code maintainability, scalability, and performance. Discover how these patterns equip businesses with cleaner and more maintainable code, promote team collaboration, reduce errors, and save time and costs. This book provides a comprehensive view of design patterns in modern (ES6+) JavaScript with real-world examples of their deployment in professional settings. You’ll start by learning how to use creational, structural, and behavioral design patterns in idiomatic JavaScript, and then shift focus to the architecture and UI patterns. Here, you’ll learn how to apply patterns for libraries such as React and extend them further to general web frontend and micro frontend approaches. The last section of the book introduces and illustrates sets of performance and security patterns, including messaging and events, asset and JavaScript loading strategies, and asynchronous programming performance patterns. Throughout the book, examples featuring React and Next.js, in addition to JavaScript and Web API examples, will help you choose and implement proven design patterns across diverse web ecosystems, transforming the way you approach development.
Table of Contents (16 chapters)
close
close
1
Part 1:Design Patterns
5
Part 2:Architecture and UI Patterns
9
Part 3:Performance and Security Patterns

Implementing the prototype pattern in JavaScript

Let’s start with a definition of the prototype pattern first.

The prototype design pattern allows us to create an instance based on another existing instance (our prototype).

In more formal terms, a prototype class exposes a clone() method. Consuming code, instead of calling new SomeClass, will call new SomeClassPrototype(someClassInstance).clone(). This method call will return a new SomeClass instance with all the values copied from someClassInstance.

Implementation

Let’s imagine a scenario where we’re building a chessboard. There are two key types of squares – white and black. In addition to this information, each square contains information such as its row, file, and which piece sits atop it.

A BoardSquare class constructor might look like the following:

class BoardSquare {
  constructor(color, row, file, startingPiece) {
    this.color = color;
    this.row = row;
    this.file = file;
  }
}

A set of useful methods on BoardSquare might be occupySquare and clearSquare, as follows:

class BoardSquare {
  // no change to the rest of the class
  occupySquare(piece) {
    this.piece = piece;
  }
  clearSquare() {
    this.piece = null;
  }
}

Instantiating BoardSquare is quite cumbersome, due to all its properties:

const whiteSquare = new BoardSquare('white');
const whiteSquareTwo = new BoardSquare('white');
// ...
const whiteSquareLast = new BoardSquare('white');

Note the repetition of arguments being passed to new BoardSquare, which will cause issues if we want to change all board squares to black. We would need to change the parameter passed to each call of BoardSquare is one by one for each new BoardSquare call. This can be quite error-prone; all it takes is one hard-to-find mistake in the color value to cause a bug:

const blackSquare = new BoardSquare('black');
const blackSquareTwo = new BoardSquare('black');
// ...
const blackSquareLast = new BoardSquare('black');

Implementing our instantiation logic using a classical prototype looks as follows. We need a BoardSquarePrototype class; its constructor takes a prototype property, which it stores on the instance. BoardSquarePrototype exposes a clone() method that takes no arguments and returns a BoardSquare instance, with all the properties of prototype copied onto it:

class BoardSquarePrototype {
  constructor(prototype) {
    this.prototype = prototype;
  }
  clone() {
    const boardSquare = new BoardSquare();
    boardSquare.color = this.prototype.color;
    boardSquare.row = this.prototype.row;
    boardSquare.file = this.prototype.file;
    return boardSquare;
  }
}

Using BoardSquarePrototype requires the following steps:

  1. First, we want an instance of BoardSquare to initialize – in this case, with 'white'. It will then be passed as the prototype property during the BoardSquarePrototype constructor call:
    const whiteSquare = new BoardSquare('white');
    const whiteSquarePrototype = new BoardSquarePrototype
      (whiteSquare);
  2. We can then use whiteSquarePrototype with .clone() to create our copies of whiteSquare. Note that color is copied over but each call to clone() returns a new instance.
    const whiteSquareTwo = whiteSquarePrototype.clone();
    // ...
    const whiteSquareLast = whiteSquarePrototype.clone();
    console.assert(
      whiteSquare.color === whiteSquareTwo.color &&
        whiteSquareTwo.color === whiteSquareLast.color,
      'Prototype.clone()-ed instances have the same color
         as the prototype'
    );
    console.assert(
      whiteSquare !== whiteSquareTwo &&
        whiteSquare !== whiteSquareLast &&
        whiteSquareTwo !== whiteSquareLast,
      'each Prototype.clone() call outputs a different
         instances'
    );

Per the assertions in the code, the cloned instances contain the same value for color but are different instances of the Square object.

A use case

To illustrate what it would take to change from a white square to a black square, let’s look at some sample code where 'white' is not referenced in the variable names:

const boardSquare = new BoardSquare('white');
const boardSquarePrototype = new BoardSquarePrototype(boardSquare);
const boardSquareTwo = boardSquarePrototype.clone();
// ...
const boardSquareLast = boardSquarePrototype.clone();
console.assert(
  boardSquareTwo.color === 'white' &&
    boardSquare.color === boardSquareTwo.color &&
    boardSquareTwo.color === boardSquareLast.color,
  'Prototype.clone()-ed instances have the same color as
     the prototype'
);
console.assert(
  boardSquare !== boardSquareTwo &&
    boardSquare !== boardSquareLast &&
    boardSquareTwo !== boardSquareLast,
  'each Prototype.clone() call outputs a different
    instances'
);

In this scenario, we would only have to change the color value passed to BoardSquare to change the color of all the instances cloned from the prototype:

const boardSquare = new BoardSquare('black');
// rest of the code stays the same
console.assert(
  boardSquareTwo.color === 'black' &&
    boardSquare.color === boardSquareTwo.color &&
    boardSquareTwo.color === boardSquareLast.color,
  'Prototype.clone()-ed instances have the same color as
     the prototype'
);
console.assert(
  boardSquare !== boardSquareTwo &&
    boardSquare !== boardSquareLast &&
    boardSquareTwo !== boardSquareLast,
  'each Prototype.clone() call outputs a different
     instances'
);

The prototype pattern is useful in situations where a “template” for the object instances is useful. It’s a good pattern to create a “default object” but with custom values. It allows faster and easier changes, since they are implemented once on the template object but are applied to all clone()-ed instances.

Increasing robustness to change in the prototype’s instance variables with modern JavaScript

There are improvements we can make to our prototype implementation in JavaScript.

The first is in the clone() method. To make our prototype class robust to changes in the prototype’s constructor/instance variables, we should avoid copying the properties one by one.

For example, if we add a new startingPiece parameter that the BoardSquare constructor takes and sets the piece instance variable to, our current implementation of BoardSquarePrototype will fail to copy it, since it only copies color, row, and file:

class BoardSquare {
  constructor(color, row, file, startingPiece) {
    this.color = color;
    this.row = row;
    this.file = file;
    this.piece = startingPiece;
  }
  // same rest of the class
}
const boardSquare = new BoardSquare('white', 1, 'A',
  'king');
const boardSquarePrototype = new BoardSquarePrototype
  (boardSquare);
const otherBoardSquare = boardSquarePrototype.clone();
console.assert(
  otherBoardSquare.piece === undefined,
  'prototype.piece was not copied over'
);

Note

Reference for Object.assign: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign.

If we amend our BoardSquarePrototype class to use Object.assign(new BoardSquare(), this.prototype), it will copy all the enumerable properties of prototype:

class BoardSquarePrototype {
  constructor(prototype) {
    this.prototype = prototype;
  }
  clone() {
    return Object.assign(new BoardSquare(), this.prototype);
  }
}
const boardSquare = new BoardSquare('white', 1, 'A',
  'king');
const boardSquarePrototype = new BoardSquarePrototype
  (boardSquare);
const otherBoardSquare = boardSquarePrototype.clone();
console.assert(
  otherBoardSquare.piece === 'king' &&
    otherBoardSquare.piece === boardSquare.piece,
  'prototype.piece was copied over'
);

The prototype pattern without classes in JavaScript

For historical reasons, JavaScript has a prototype concept deeply embedded into the language. In fact, classes were introduced much later into the ECMAScript standard, with ECMAScript 6, which was released in 2015 (for reference, ECMAScript 1 was published in 1997).

This is why a lot of JavaScript completely forgoes the use of classes. The JavaScript “object prototype” can be used to make objects inherit methods and variables from each other.

One way to clone objects is by using the Object.create to clone objects with their methods. This relies on the JavaScript prototype system:

const square = {
  color: 'white',
  occupySquare(piece) {
    this.piece = piece;
  },
  clearSquare() {
    this.piece = null;
  },
};
const otherSquare = Object.create(square);

One subtlety here is that Object.create does not actually copy anything; it simply creates a new object and sets its prototype to square. This means that if properties are not found on otherSquare, they’re accessed on square:

console.assert(otherSquare.__proto__ === square, 'uses JS
  prototype');
console.assert(
  otherSquare.occupySquare === square.occupySquare &&
    otherSquare.clearSquare === square.clearSquare,
  "methods are not copied, they're 'inherited' using the
     prototype"
);
delete otherSquare.color;
console.assert(
  otherSquare.color === 'white' && otherSquare.color ===
    square.color,
  'data fields are also inherited'
);

A further note on the JavaScript prototype, and its existence before classes were part of JavaScript, is that subclassing in JavaScript is another syntax for setting an object’s prototype. Have a look at the following extends example. BlackSquare extends Square sets the prototype.__proto__ property of BlackSquare to Square.prototype:

class Square {
  constructor() {}
  occupySquare(piece) {
    this.piece = piece;
  }
  clearSquare() {
    this.piece = null;
  }
}
class BlackSquare extends Square {
  constructor() {
    super();
    this.color = 'black';
  }
}
console.assert(
  BlackSquare.prototype.__proto__ === Square.prototype,
  'subclass prototype has prototype of superclass'
);

In this section, we learned how to implement the prototype pattern with a prototype class that exposes a clone() method, which code situations the prototype patterns can help with, and how to further improve our prototype implementation with modern JavaScript features. We also covered the JavaScript “prototype,” why it exists, and its relationship with the prototype design pattern.

In the next part of the chapter, we’ll look at another creational design pattern, the singleton design pattern, with some implementation approaches in JavaScript and its use cases.

Visually different images
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
JavaScript Design Patterns
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist download Download options font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon