JavaScript Design Pattern - Singleton Pattern

✅ Common Pattern of the Recurring Problem

Sometimes a program has to restrict the number of instances of a class to one, where

  • an instantiation is costly (e.g. DB connection)
  • or a single object has to coordinate other objects across a system (e.g. config object)

✅ Solution

Singleton pattern restricts the number of class instantiations to one with the following strategies:

  • It creates a new instance of the class if one doesn’t exist. If there is a pre-existing instance, it simply returns a reference to that object.
  • The single instance can be accessed globally throughout the application from a well-known access point.
  • This single instance is unmodifiable

Singleton pattern has the following tradeoffs:

🟢 memory-efficient; There is no need for each and every new instance to take up its own space since only one instance is created and referenced throughout the application,

🟢 controlled initialization

Consider an object instantiated by initialize() method.

const MyDatabase = (function() {

  // (...)
  
  return {
    // (...)
    initialize: function (uri) {
      if (!instance) {
        if (isSQL(uri)) instance = new MySQL();
        else if (isNoSQL(uri)) instance = new MyFirebase()
      }
      return instance;
    }
    // (...)
  }
})();

Here, initialize() serves as a factory method and provides global access to MyDatabase instance. By creating an instance of the database with MyDatabase.initialize('MY_DB_URI') instead of directly by new MyDatabase('MY_DB_URI'),  one can add extra logic to instantiation.

  • You can defer the instantiation of expensive classes via deferred/lazy instantiation.
  • The sole class MyDatabase can be extended by subclasses. In the above code, MySQL and Firebase should implement the same interface as MyDatabase and are thus subclasses of MyDatabase. By accessing MyDatabase via initialize(), client code can use extended subclasses without modifying each access point, for example from MyDatabase to MyFirebase.

❌ tight coupling; However, one should be wary of the singleton pattern, since it could be an indication that modules are tightly coupled and one change in this module can affect another module.

❌ dependency hiding: Also, when it is not made clear that an imported module is a singleton, it might lead to unexpected behavior and propagate throughout the application.

Singleton Pattern (Function implementation)

Note that I didn't call new MyDatabase('MY_DB_URI') directly. Instead, I invoked global access to the instance (getInstance() in the example). getInstance() checks if an instance of MyDatabase already exists. If not, it creates one, and if there is one already, it returns the existing DB instance. Then, it export defaults the instance to make it globally accessible. Upon export, db.js module freezes the instance with JavaScript's built-in Object.freeze() method to ensure that the one and only instance is not modifiable.

Singleton Pattern (Class implementation)

Here, note that the explicit global access in the previous example(.getInstance()) is replaced by built-in constructor() syntax.

✅ Language Integration

ES2015 Modules are singletons by default. Therefore, one doesn't have to explicitly implement the global, non-modifiable behavior of the exported module.

In the above example, index.html imports file1.js and file2.js in order, which respectively imports db.js and connects to the database instance, and increments connectionCount by 1. Both files are looking at the same instance of MyDatabase . As a result, even though file2.js invokes connect() method only once, connectionCount is now 2, on top of the connection made by file1.js.

References

Learning JavaScript Design Patterns, Osmani, Addy. O'Reilly
Hallie, Lydia. (2022, August 18). A Tour of JavaScript & React Patterns [video file]. Retrieved from https://frontendmasters.com/courses/tour-js-patterns/

Subscribe to go-kahlo-lee

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe