20 Software Design Patterns That Every JavaScript Developer Should Know (Part 1)
Most popular patterns with JavaScript code examples
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.
- To use the
__proto__
property. This approach is for ancient browsers that are not supported now. I will not describe this as a legacy. - 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. - 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 instanceB
are 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!