20 Software Design Patterns That Every JavaScript Developer Should Know (Part 1)

Most popular patterns with JavaScript code examples

Volodymyr Golosay
From Dev to Devs

--

Software Design Patterns

JavaScript is one of the most popular programming languages in the world and is widely used to create interactive and dynamic websites. As a JavaScript developer, you must be familiar with various design patterns that can help you write clean, maintainable, and scalable code.

In this article, we will be discussing 20 software design patterns that every JavaScript developer should know. Honestly, I wanted to write about the Top 10 design patterns but could not decide what should be excluded.

So please make yourself comfortable, and let’s go.

The Module pattern

used for encapsulation and organization of code

The Module pattern is a way to organize your JavaScript code and make it more modular and maintainable. In addition, it’s a great way to create a private and public interface for your code to keep some things hidden and expose what’s necessary to other parts of the codebase.

This pattern uses closures to create an object with a private state and a public API. The private state and methods are only accessible within the closure, while the public API is returned and can be used by other parts of the code.

Here is an example of the Module pattern in JavaScript:

const myModule = (function () {
let privateVariable = "I am a private variable";
let privateMethod = function() {
console.log(privateVariable);
}

return {
publicMethod: function() {
privateMethod();
}
}
})();

console.log(myModule.privateVariable); //undefined
console.log(myModule.privateMethod); //undefined
myModule.publicMethod(); // "I am a private variable"

In this example, we have a function that creates a private variable and method and then returns an object with a public method. The public method can access the private variable and method, but the private variable and method can’t be accessed directly outside the function. This way, you can keep the private stuff private and only expose the parts of your code you want others to use.

It is just one way how to implement the module pattern. You can also use the export and import statement in es6, a modern approach to implementing the same functionality.

Constructor pattern

used for creating objects with a specific structure

The Constructor pattern is a way to create objects with a specific structure in JavaScript. It’s often used to create multiple instances of an object with the same properties and methods. The pattern uses a constructor function to create new objects and the new keyword to instantiate those objects.

There are two ways to implement a constructor pattern in JavaScript: old and modern. I will show both implementations.

The old one:

function Person(name, age) {
this.name = name;
this.age = age;
}

Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}

let john = new Person("John", 25);
let jane = new Person("Jane", 30);

console.log(john.name); // "John"
console.log(jane.age); // 30

john.sayHello(); // "Hello, my name is John and I am 25 years old."
jane.sayHello(); // "Hello, my name is Jane and I am 30 years old."

In this example, we define a Person constructor function that takes in a name and age as arguments. The constructor function creates an object with the properties name and age, and assigns them the values passed in as arguments. We also added a method sayHello to the Person.prototype which is a shared property among all the instances created using new keyword.

As you can see, we created two new instances of the Person object and assigned them to the variables john and jane.

The Constructor pattern is a great way to create multiple instances of an object with the same structure. It also makes it easy to create new instances of the object using the new keyword, and it’s an excellent way to set up an inheritance hierarchy. Because later, you can define more constructor functions that will inherit everything from the Person function and contain their additional variables and methods.

The modern one:

In modern JavaScript, you can use the class syntax to create objects with a specific structure using the Constructor pattern.

Here’s an example of how you might use the class syntax to create the same Person object:

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}

let john = new Person("John", 25);
let jane = new Person("Jane", 30);

console.log(john.name); // "John"
console.log(jane.age); // 30

john.sayHello(); // "Hello, my name is John and I am 25 years old."
jane.sayHello(); // "Hello, my name is Jane and I am 30 years old."

As you can see, the class syntax is more modern and easier to read. It’s also more similar to the syntax of other object-oriented languages. The constructor function defines the class properties, and the methods are defined inside the class.

It’s important to note that the class syntax is just a syntax sugar over the constructor pattern and the inheritance mechanism is the same.

Prototype pattern

used for creating objects that inherit from a shared prototype

The Prototype pattern is a way to create objects inherited from a shared prototype in JavaScript. It allows you to create new objects that inherit the properties and methods of an existing object without having to define them again.

There is a minimum of 3 ways how to implement inheritance in JavaScript.

  1. To use the __proto__ property. This approach is for ancient browsers that are not supported now. I will not describe this as a legacy.
  2. Uses the Object.create() method to create new objects that inherit from a prototype object. This method is also rare, but I will show it.
  3. Uses the extends keyword to inherit from other classes. The modern and easiest way.

Here’s an example of how you might use the Prototype pattern with the Object.create()

let personPrototype = {
sayHello: function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
};

let john = Object.create(personPrototype, {
name: { value: "John" },
age: { value: 25 }
});

let jane = Object.create(personPrototype, {
name: { value: "Jane" },
age: { value: 30 }
});

console.log(john.name); // "John"
console.log(jane.age); // 30

john.sayHello(); // "Hello, my name is John and I am 25 years old."
jane.sayHello(); // "Hello, my name is Jane and I am 30 years old."

In this example, we define a personPrototype object that has a sayHello method. We then create two new objects, john and jane, using the Object.create() method and pass in the personPrototype object as the first argument. The Object.create() method creates new objects inherited from the prototype object passed in as the first argument. We also define properties name and age for each object by passing a property descriptor in the second argument.

In modern JavaScript, the class syntax provides an easier way to handle inheritance. It allows you to use the extends keyword to inherit from other classes and the super keyword to call the parent class's constructor method.

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}

class Employee extends Person {
constructor(name, age, job) {
super(name, age);
this.job = job;
}

sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old. I work as a ${this.job}.`);
}
}

let john = new Employee("John", 25, "Developer");
john.sayHello(); // "Hello, my name is John and I am 25 years old. I work as a Developer."

The extends keyword allows the Employee class to inherit the properties and methods of the Person class, and the super keyword allows us to call the parent class's constructor method to set the name and age properties.

The Prototype pattern is a good way to create objects with common properties and methods, allowing you to easily create new objects inherited from a prototype.

Revealing module pattern

a variation of the module pattern where only certain methods and properties are revealed to the public

The Revealing Module pattern is a variation of the Module pattern in JavaScript that allows you to reveal only the properties and methods you want to make public and keep the rest of the properties and methods private. This pattern uses closures and the revealing pattern to create an object with a private state and a public API.

Here is an example of how you might use the Revealing Module pattern:

const myModule = (function () {
let privateVariable = "I am a private variable";
let privateMethod = function() {
console.log(privateVariable);
}

let publicMethod = function() {
privateMethod();
}

return {
publicMethod: publicMethod
}
})();

console.log(myModule.privateVariable); //undefined
console.log(myModule.privateMethod); //undefined
myModule.publicMethod(); // "I am a private variable"

The publicMethod is the only returned method and can be used by other parts of the code. When myModule.publicMethod() is called, it calls the privateMethod and logs the private variable to the console.

The Revealing Module pattern is a powerful way to create JavaScript encapsulation and helps keep your code organized and maintainable.

However, when used in combination with TypeScript, it becomes even more powerful, as TypeScript provides access modifiers like public, private, and protected, which can be used to define the accessibility of properties and methods. This makes it even easier to create encapsulation and maintain the public API of a module.

Singleton pattern

ensures that a class has only one instance with a global point of access to it

The Singleton pattern is a design pattern that ensures that a class has only one instance and provides a global access point to that instance. It’s often used when a single instance of a class needs to control the action throughout an application. The Singleton pattern can be implemented in JavaScript using the Module and Revealing Module patterns.

const Singleton = (function() {
let instance;

function createInstance() {
const object = new Object({name: 'singleton'});
return object;
}

return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
}
})();

const instanceA = Singleton.getInstance();
const instanceB = Singleton.getInstance();
console.log(instanceA === instanceB); // true

The getInstance function is the only method returned and can be used by other parts of the code. This method will check if the instance already exists. If not, it will create it and return it.

When we create two instances of the Singleton class, instanceA and instanceBare strictly equal. This means both instances refer to the same object and share the same properties and methods.

This is an excellent way of controlling the number of class instances in your application. It helps in scenarios where you want to ensure that only one class instance is created throughout the application's life cycle.

In this instance, we can keep some state and share across different classes that can be recreated multiple times, but the data shared between them will be stable.

Currently, we covered 5 of the 20 most used JavaScript patterns. For a single article to cover more is too overwhelming. That’s why I will write 3 more articles with the rest patterns.

If you like material like this, follow me and subscribe to receive notifications about the next articles.

Thanks for reading!

--

--

Volodymyr Golosay
From Dev to Devs

Lead software engineer, indie developer, and tech enthusiast. I write about technology, SaaS, and the latest development trends.