An Extensive Guide To JavaScript Design Patterns
An Extensive Guide To JavaScript Design Patterns
An Extensive Guide
JavaScript Tutorials
to JavaScript Design
Patterns
Kumar Harsh, October 23, 2023
It is, of course, not convenient to solve these problems again and again.
https://kinsta.com/blog/javascript-design-patterns/ 1/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
In this guide, we will take a look at what JavaScript design patterns are and
how to use them in your JavaScript apps.
Table of Contents
What Is a JavaScript Design Pattern?
What Is a JavaScript
Design Pattern?
JavaScript design patterns are repeatable template solutions for frequently
occurring problems in JavaScript app development.
The idea is simple: Programmers all around the world, since the dawn of
development, have faced sets of recurring issues when developing apps.
Over time, some developers chose to document tried and tested ways to
tackle these issues so others could refer back to the solutions with ease.
As more and more developers chose to use these solutions and recognized
their efficiency in solving their problems, they became accepted as a
https://kinsta.com/blog/javascript-design-patterns/ 2/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
standard way of problem-solving and were given the name “design patterns.”
Subscribe
Types of JavaScript
Design Patterns
Here are some of the most popular classifications of JavaScript design
patterns.
Creational
Creational design patterns are those that help solve problems around
creating and managing new object instances in JavaScript. It can be as
simple as limiting a class to having just one object or as complex as defining
an intricate method of handpicking and adding each feature in a JavaScript
object.
https://kinsta.com/blog/javascript-design-patterns/ 3/66
Structural
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Structural design patterns are those that help solve problems around
managing the structure (or schema) of JavaScript objects. These problems
could include creating a relationship between two unlike objects or
abstracting some features of an object away forspecific users.
Behavioral
Behavioral design patterns are those that help solve problems around how
control (and responsibility) is passed between various objects. These
problems could involve controlling access to a linked list or establishing a
single entity that can control access to multiple types of objects.
Concurrency
Concurrency design patterns are those that help solve problems around
multi-threading and multitasking. These problems could entail maintaining
an active object among multiple available objects or handling multiple
events supplied to a system by demultiplexing incoming input and handling
it piece by piece.
Architectural
Architectural design patterns are those that help solve problems around
software design in a broad sense. These generally are related to how to
https://kinsta.com/blog/javascript-design-patterns/ 4/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
design your system and ensure high availability, mitigate risks, and avoid
performance bottlenecks.
Elements of a Design
Pattern
Almost all design patterns can be broken down into a set of four important
components. They are:
If you’re looking to learn more about design patterns and their inception,
MSU has some succinct study material that you can refer to.
https://kinsta.com/blog/javascript-design-patterns/ 5/66
Why Should You Use
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Design Patterns?
There are multiple reasons why you would want to use design patterns:
They’re tried and tested: With a design pattern, you have a tried-and-
tested solution to your problem (as long as the design pattern fits the
description of your problem). You don’t have to waste time looking for
alternate fixes, and you can rest assured that you have a solution that
takes care of basic performance optimization for you.
They’re easy to understand: Design patterns are meant to be small,
simple, and easy to understand. You do not need to be a specialized
programmer working in a specific industry for decades to understand
which design pattern to use. They’re purposefully generic (not limited to
any particular programming language) and can be understood by
anyone who has sufficient problem-solving skills. This also helps when
you have a change of hands in your tech team: A piece of code that
relies on a design pattern is easier to understand for any new software
developer.
They save time and app size: One of the biggest benefits of relying on a
standard set of solutions is that they will help you save time when
implementing them. There’s a good chance that your entire
development team knows design patterns well, so it will be easier for
them to plan, communicate, and collaborate when implementing them.
https://kinsta.com/blog/javascript-design-patterns/ 6/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Tried and tested solutions mean there’s a good chance you will not end
up leaking any resources or taking a detour while building some feature,
saving you both time and space. Also, most programming languages
provide you with standard template libraries that already implement
some common design patterns like Iterator and Observer.
View Pricing
Creational
Let’s start the discussion with some fundamental, easy-to-learn creational
design patterns.
https://kinsta.com/blog/javascript-design-patterns/ 7/66
1. Singleton
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
The Singleton pattern is one of the most commonly used design patterns
across the software development industry. The problem that it aims to solve
is to maintain only a single instance of a class. This can come in handy when
instantiating objects that are resource-intensive, such as database handlers.
function SingletonFoo() {
// For our reference, let's create a counter that will track the numb
let count = 0;
function printCount() {
console.log("Number of instances: " + count);
}
function init() {
// For our reference, we'll increase the count by one whenever in
count++;
function createInstance() {
if (fooInstance == null) {
fooInstance = init();
}
return fooInstance;
}
function closeInstance() {
count--;
fooInstance = null;
}
return {
initialize: createInstance,
close: closeInstance,
printCount: printCount
}
}
https://kinsta.com/blog/javascript-design-patterns/ 8/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
foo.printCount() // Prints 0
foo.initialize()
foo.printCount() // Prints 1
foo.initialize()
foo.printCount() // Still prints 1
foo.initialize()
foo.printCount() // Still 1
foo.close()
foo.printCount() // Prints 0
While it serves the purpose well, the Singleton pattern is known to make
debugging difficult since it masks dependencies and controls the access to
initializing or destroying a class’s instances.
2. Factory Method
The Factory method is also one of the most popular design patterns. The
problem that the Factory method aims to solve is creating objects without
using the conventional constructor. Instead, it takes in the configuration (or
description) of the object that you want and returns the newly created
object.
function Factory() {
this.createDog = function (breed) {
let dog;
dog.breed = breed;
dog.printInfo = function () {
console.log("\n\nBreed: " + dog.breed + "\nShedding Level (ou
https://kinsta.com/blog/javascript-design-patterns/ 9/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
}
return dog;
}
}
function Labrador() {
this.sheddingLevel = 4
this.coatLength = "short"
this.coatType = "double"
}
function Bulldog() {
this.sheddingLevel = 3
this.coatLength = "short"
this.coatType = "smooth"
}
function GoldenRetriever() {
this.sheddingLevel = 4
this.coatLength = "medium"
this.coatType = "double"
}
function GermanShepherd() {
this.sheddingLevel = 4
this.coatLength = "medium"
this.coatType = "double"
}
function run() {
dogs.push(factory.createDog("labrador"));
dogs.push(factory.createDog("bulldog"));
dogs.push(factory.createDog("golden retriever"));
dogs.push(factory.createDog("german shepherd"));
run()
/**
Output:
Breed: labrador
Shedding Level (out of 5): 4
Coat Length: short
Coat Type: double
https://kinsta.com/blog/javascript-design-patterns/ 10/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Breed: bulldog
Shedding Level (out of 5): 3
Coat Length: short
Coat Type: smooth
The Factory design pattern controls how the objects will be created and
provides you with a quick way of creating new objects, as well as a uniform
interface that defines the properties that your objects will have. You can add
as many dog breeds as you want, but as long as the methods and properties
exposed by the breed types remain the same, they will work flawlessly.
However, note that the Factory pattern can often lead to a large number of
classes that can be difficult to manage.
3. Abstract Factory
The Abstract Factory takes the Factory method up a level by making factories
abstract and thus replaceable without the calling environment knowing the
exact factory used or its internal workings. The calling environment only
knows that all the factories have a set of common methods that it can call
to perform the instantiation action.
dog.breed = breed;
dog.printInfo = function () {
console.log("\n\nType: " + dog.type + "\nBreed: " + dog.breed
}
return dog;
}
}
cat.breed = breed;
cat.printInfo = function () {
console.log("\n\nType: " + cat.type + "\nBreed: " + cat.breed
}
return cat;
}
}
function Pug() {
this.type = "dog"
this.size = "small"
}
function Ragdoll() {
this.type = "cat"
this.size = "large"
}
https://kinsta.com/blog/javascript-design-patterns/ 12/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
function Singapura() {
this.type = "cat"
this.size = "small"
}
function run() {
// Create a common petFactory that can produce both cats and dogs
// Set it to produce dogs first
let petFactory = dogFactory;
pets.push(petFactory.createPet("labrador"));
pets.push(petFactory.createPet("pug"));
pets.push(petFactory.createPet("ragdoll"));
pets.push(petFactory.createPet("singapura"));
run()
/**
Output:
Type: dog
Breed: labrador
Size: large
Type: dog
Breed: pug
Size: small
Type: cat
Breed: ragdoll
Size: large
Type: cat
Breed: singapura
Size: small
https://kinsta.com/blog/javascript-design-patterns/ 13/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
*/
The Abstract Factory pattern makes it easy for you to exchange concrete
factories easily, and it helps promote uniformity between factories and the
products created. However, it can become difficult to introduce new kinds of
products since you’d have to make changes in multiple classes to
accommodate new methods/properties.
4. Builder
The Builder pattern is one of the most complex yet flexible creational
JavaScript design patterns. It allows you to build each feature into your
product one by one, providing you full control over how your object is built
while still abstracting away the internal details.
In the intricate example below, you’ll see the Builder design pattern in action
along with Director to help make Pizzas!
this.printInfo = function() {
console.log("This pizza has " + this.base + " base with " + t
+ (this.cheese !== undefined ? "with cheese. " : "without che
+ (this.toppings.length !== 0 ? "It has the following topping
}
}
https://kinsta.com/blog/javascript-design-patterns/ 14/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// You can request the PizzaBuilder (/chef) to perform any of the fol
return {
addFlatbreadBase: function() {
base = "flatbread"
return this;
},
addTomatoSauce: function() {
sauce = "tomato"
return this;
},
addAlfredoSauce: function() {
sauce = "alfredo"
return this;
},
addCheese: function() {
cheese = "parmesan"
return this;
},
addOlives: function() {
toppings.push("olives")
return this
},
addJalapeno: function() {
toppings.push("jalapeno")
return this
},
cook: function() {
if (base === null){
console.log("Can't make a pizza without a base")
return
}
return new Pizza(base, sauce, cheese, toppings)
}
}
https://kinsta.com/blog/javascript-design-patterns/ 15/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
}
}
}
run()
You can pair up the Builder with a Director, as shown by the PizzaShop class
in the example above, to predefine a set of steps to follow every time to
build a standard variant of your product, i.e., a specific recipe for your pizzas.
The only issue with this design pattern is that it is quite complex to set up
and maintain. Adding new features this way is simpler than the Factory
method, though.
5. Prototype
The Prototype design pattern is a quick and simple way of creating new
objects from existing objects by cloning them.
https://kinsta.com/blog/javascript-design-patterns/ 16/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
In the example below, you’ll see how you can use the Prototype pattern to
create new documents based on a set template document:
this.addText = function(text) {
this.text += text
}
// A protype (or template) for creating new blank documents with boilerp
function DocumentPrototype(baseDocument) {
this.baseDocument = baseDocument
document.header = this.baseDocument.header
document.footer = this.baseDocument.footer
document.pages = this.baseDocument.pages
document.text = this.baseDocument.text
return document
}
}
function run() {
// Create a document to use as the base for the prototype
let baseDocument = new Document()
https://kinsta.com/blog/javascript-design-patterns/ 17/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
baseDocument.addText("This text was added before cloning and will be
doc2.printInfo()
/** Output:
Header: Acme Co
Footer: For internal use only
Pages: 2
Text: This text was added before cloning and will be common in bo
*/
}
run()
The Prototype method works great for cases where a large part of your
objects share the same values, or when creating a new object altogether is
quite costly. However, it feels like overkill in cases where you don’t need
more than a few instances of the class.
Structural
Structural design patterns help you organize your business logic by
providing tried and tested ways of structuring your classes. There are a
variety of structural design patterns that each cater to unique use cases.
https://kinsta.com/blog/javascript-design-patterns/ 18/66
6. Adapter
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
The Adapter design pattern provides you with an abstraction that bridges
the gap between the new class’s methods and properties and the old class’s
methods and properties. It has the same interface as the old class, but it
contains logic to map old methods to the new methods to execute similar
operations. This is similar to how a power plug socket acts as an adapter
between a US-style plug and a European-style plug.
Here’s an example:
// Old bot
function Robot() {
this.walk = function(numberOfSteps) {
// code to make the robot walk
console.log("walked " + numberOfSteps + " steps")
}
this.sit = function() {
// code to make the robot sit
console.log("sit")
}
// New bot that does not have the walk function anymore
// but instead has functions to control each step independently
function AdvancedRobot(botName) {
// the new bot has a name as well
this.name = botName
https://kinsta.com/blog/javascript-design-patterns/ 19/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
this.sit = function() {
// code to make the robot sit
console.log("sit")
}
this.rightStepForward = function() {
// code to take 1 step from right leg forward
console.log("right step forward")
}
this.leftStepForward = function () {
// code to take 1 step from left leg forward
console.log("left step forward")
}
}
function RobotAdapter(botName) {
// No references to the old interfact since that is usually
// phased out of development
const robot = new AdvancedRobot(botName)
if (i % 2 === 0) {
robot.rightStepForward()
} else {
robot.leftStepForward()
}
}
}
this.sit = robot.sit
function run() {
robot.sit()
// Output: sit
robot.walk(5)
// Output: walked 5 steps
robot.sit()
// Output: sit
robot.walk(5)
// Output:
// right step forward
https://kinsta.com/blog/javascript-design-patterns/ 20/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// left step forward
// right step forward
// left step forward
// right step forward
run()
The main issue with this design pattern is that it adds complexity to your
source code. You already needed to maintain two different classes, and now
you have another class — the Adapter — to maintain.
7. Bridge
Expanding upon the Adapter pattern, the Bridge design pattern provides
both the class and the client with separate interfaces so that they may both
work even in cases of incompatible native interfaces.
this.decreaseVolume = function() {
// logic to decrease TV volume
}
this.mute = function() {
// logic to mute TV audio
}
}
function Speaker() {
https://kinsta.com/blog/javascript-design-patterns/ 21/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
this.increaseVolume = function() {
// logic to increase speaker volume
}
this.decreaseVolume = function() {
// logic to decrease speaker volume
}
this.mute() = function() {
// logic to mute speaker audio
}
}
this.pressVolumeUpKey = function() {
device.increaseVolume()
}
}
function AdvancedRemote(device) {
this.pressVolumeDownKey = function() {
device.decreaseVolume()
}
this.pressVolumeUpKey = function() {
device.increaseVolume()
}
this.pressMuteKey = function() {
device.mute()
}
}
function run() {
// The methods listed in pair below will have the same effect
// on their target devices
tvSimpleRemote.pressVolumeDownKey()
tvAdvancedRemote.pressVolumeDownKey()
https://kinsta.com/blog/javascript-design-patterns/ 22/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
tvSimpleRemote.pressVolumeUpKey()
tvAdvancedRemote.pressVolumeUpKey()
speakerSimpleRemote.pressVolumeDownKey()
speakerAdvancedRemote.pressVolumeDownKey()
speakerSimpleRemote.pressVolumeUpKey()
speakerAdvancedRemote.pressVolumeUpKey()
speakerAdvancedRemote.pressMuteKey()
}
As you might have already guessed, the Bridge pattern greatly increases the
complexity of the codebase. Also, most interfaces usually end up with only
one implementation in real-world use cases, so you don’t really benefit from
the code reusability much.
8. Composite
The Composite design pattern helps you structure and manage similar
objects and entities easily. The basic idea behind the Composite pattern is
that the objects and their logical containers can be represented using a
single abstract class (that can store data/methods related to the object and
references to itself for the container).
It makes the most sense to use the Composite pattern when your data
model resembles a tree structure. However, you shouldn’t try to turn a non-
tree data model into a tree-like data model just for the sake of using the
Composite pattern, as doing so can often take away a lot of flexibility.
In the example below, you’ll see how you can use the Composite design
pattern to construct a packaging system for ecommerce products that can
also calculate the total order value per package:
https://kinsta.com/blog/javascript-design-patterns/ 23/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
this.getTotalPrice = function() {
return this.price
}
}
// Helper function to get the total count of the items in the box
this.getTotalCount = function() {
return this.contents.length
}
return totalPrice
}
}
function run() {
https://kinsta.com/blog/javascript-design-patterns/ 24/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// and finally, put them into one big box for convenient shipping
const package = new Box('package')
package.add(electronicsBox)
package.add(stationeryBox)
run()
The biggest downside to using the Composite pattern is that changes to the
component interfaces can be very challenging in the future. Designing the
interfaces takes time and effort, and the tree-like nature of the data model
can make it very tough to make changes as you wish.
9. Decorator
The Decorator pattern helps you add new features to existing objects by
simply wrapping them up inside a new object. It’s similar to how you can
wrap an already-wrapped gift box with new wrapping paper as many times
https://kinsta.com/blog/javascript-design-patterns/ 25/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
as you want: Each wrap allows you to add as many features as you’d like, so
it’s great on the flexibility front.
In the example below, you’ll see how the Decorator pattern helps to add
more features to a standard Customer class:
this.printInfo = function() {
console.log("Customer:\nName : " + this.name + " | Age: " + this.
}
}
this.printInfo = function() {
console.log("Decorated Customer:\nName: " + this.name + " | Age:
}
}
function run() {
let customer = new Customer("John", 25)
customer.printInfo()
// Output:
// Customer:
// Name : John | Age: 25
run()
https://kinsta.com/blog/javascript-design-patterns/ 26/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
The downsides of this pattern include high code complexity since there is
no standard pattern defined for adding new features using decorators. You
might end up with a lot of non-uniform and/or similar decorators at the end
of your software development lifecycle.
If you’re not careful while designing the decorators, you might end up
designing some decorators to be logically dependent on others. If this is not
resolved, removing or restructuring decorators later down the line can
wreak havoc on your application’s stability.
10. Facade
When building most real-world applications, the business logic usually turns
out to be quite complex by the time you are done. You might end up with
multiple objects and methods being involved in executing core operations
in your app. Maintaining track of their initializations, dependencies, the
correct order of method execution, etc., can be quite tricky and error-prone
if not done correctly.
The Facade design pattern helps you create an abstraction between the
environment that invokes the above-mentioned operations and the objects
and methods involved in completing those operations. This abstraction
houses the logic for initializing the objects, tracking their dependencies, and
other important activities. The calling environment has no information on
how an operation is executed. You can freely update the logic without
making any breaking changes to the calling client.
/**
* Let's say you're trying to build an online store. It will have multipl
* complex business logic. In the example below, you will find a tiny seg
* store composed together using the Facade design pattern. The various m
* classes are defined first of all.
*/
function CartManager() {
this.getItems = function() {
https://kinsta.com/blog/javascript-design-patterns/ 27/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// logic to return items
return []
}
this.clearCart = function() {
// logic to clear cart
}
}
function InvoiceManager() {
this.createInvoice = function(items) {
// logic to create invoice
return {}
}
this.notifyCustomerOfFailure = function(invoice) {
// logic to notify customer
}
this.updateInvoicePaymentDetails = function(paymentResult) {
// logic to update invoice after payment attempt
}
}
function PaymentProcessor() {
this.processPayment = function(invoice) {
// logic to initiate and process payment
return {}
}
}
function WarehouseManager() {
this.prepareForShipping = function(items, invoice) {
// logic to prepare the items to be shipped
}
}
this.placeOrder = function() {
let cartManager = new CartManager()
let items = cartManager.getItems()
https://kinsta.com/blog/javascript-design-patterns/ 28/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
cartManager.clearCart()
} else {
invoiceManager.notifyCustomerOfFailure(invoice)
}
}
}
onlineStore.placeOrder()
}
11. Flyweight
The Flyweight pattern helps you solve problems that involve objects with
repeating components in memory-efficient ways by helping you reuse the
common components of your object pool. This helps reduce the load on the
memory and results in faster execution times as well.
In the example below, a large sentence is stored in the memory using the
Flyweight design pattern. Instead of storing each character as it occurs, the
program identifies the set of distinct characters that have been used to
write the paragraph and their types (number or alphabet) and builds
reusable flyweights for each character that contains details of which
character and type are stored.
https://kinsta.com/blog/javascript-design-patterns/ 29/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Then the main array just stores a list of references to these flyweights in the
order that they occur in the sentence instead of storing an instance of the
character object whenever it occurs.
This reduces the memory taken by the sentence by half. Bear in mind that
this is a very basic explanation of how text processors store text.
// A simple Character class that stores the value, type, and position of
function Character(value, type, position) {
this.value = value
this.type = type
this.position = position
}
return {
get: function (value, type) {
if (flyweights[value + type] === undefined)
flyweights[value + type] = new CharacterFlyweight(value,
https://kinsta.com/blog/javascript-design-patterns/ 30/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
switch (char) {
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9": return "N"
default:
return "A"
}
}
return chars
}
return chars
}
function run() {
// Our input is a large paragraph with over 600 characters
let input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
https://kinsta.com/blog/javascript-design-patterns/ 31/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// This means that to store 656 characters, a total of
// (31 * 2 + 656 * 1 = 718) memory blocks are used instead of
// (656 * 3 = 1968) which would have used by the standard array.
// (We have assumed each variable to take up one memory block for sim
// This may vary in real-life scenarios.)
run()
As you may have already noticed, the Flyweight pattern adds to the
complexity of your software design by not being particularly intuitive. So, if
saving memory isn’t a pressing concern for your app, Flyweight’s added
complexity can do more bad than good.
12. Proxy
The Proxy pattern helps you substitute an object for another object. In other
terms, proxy objects can take the place of actual objects (that they’re a
proxy of) and control access to the object. These proxy objects can be used
to perform some actions before or after an invocation request is passed on
to the actual object.
function DatabaseHandler() {
const data = {}
https://kinsta.com/blog/javascript-design-patterns/ 32/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
this.get = function (key, val) {
return data[key]
}
this.remove = function (key) {
data[key] = null;
}
function DatabaseProxy(databaseInstance) {
databaseInstance.set(key, val)
}
return databaseInstance.get(key)
}
return databaseInstance.remove(key)
}
function run() {
https://kinsta.com/blog/javascript-design-patterns/ 33/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
let databaseInstance = new DatabaseHandler()
databaseInstance.set("foo", "bar")
databaseInstance.set("foo", undefined)
console.log("#1: " + databaseInstance.get("foo"))
// #1: undefined
databaseInstance.set("", "something")
databaseInstance.remove("foo")
console.log("#3: " + databaseInstance.get("foo"))
// #3: null
databaseInstance.remove("foo")
// databaseInstance.remove("baz")
proxy.set("foo", "bar")
proxy.set("foo", undefined)
// Proxy jumps in:
// Output: Setting value to undefined not allowed!
proxy.set("", "something")
// Proxy jumps in again
// Output: Invalid input
proxy.remove("foo")
proxy.remove("foo")
// Proxy output: Element removed already
proxy.remove("baz")
// Proxy output: Element not added
https://kinsta.com/blog/javascript-design-patterns/ 34/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
run()
This design pattern is commonly used across the industry and helps to
implement pre- and post-execution operations easily. However, just like any
other design pattern, it also adds complexity to your codebase, so try not to
use it if you don’t really need it.
You’ll also want to keep in mind that since an additional object is involved
when making calls to your actual object, there might be some latency due to
the added processing operations. Optimizing your main object’s
performance now also involves optimizing your proxy’s methods for
performance.
Behavioral
Behavioral design patterns help you solve problems around how objects
interact with one another. This can involve sharing or passing
responsibility/control between objects to complete set operations. It can
also involve passing/sharing data across multiple objects in the most
efficient way possible.
https://kinsta.com/blog/javascript-design-patterns/ 35/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Below you will see an example of a complaint escalation using the Chain of
Responsibility pattern. The complaint will be handled by the handlers on the
basis of its severity:
function run() {
// Create an instance of the base level handler
let customerSupport = new Representative()
run()
The obvious issue with this design is that it’s linear, so there can be some
latency in handling an operation when a large number of handlers are
chained to one another.
Keeping track of all handlers can be another pain point, as it can get quite
messy after a certain number of handlers. Debugging is yet another
nightmare as each request can end on a different handler, making it difficult
for you to standardize the logging and debugging process.
14. Iterator
The Iterator pattern is quite simple and is very commonly used in almost all
modern object-oriented languages. If you find yourself faced with the task of
going through a list of objects that aren’t all the same type, then normal
iteration methods, such as for loops, can get quite messy — especially if
you’re also writing business logic inside them.
The Iterator pattern can help you isolate the iteration and processing logic
for your lists from the main business logic.
Here’s how you can use it on a rather basic list with multiple types of
elements:
https://kinsta.com/blog/javascript-design-patterns/ 37/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
function run() {
// A complex list with elements of multiple data types
let list = ["Lorem ipsum", 9, ["lorem ipsum dolor", true], false]
// Use the custom iterator to pass an effect that will run for each e
iterator.forEach(function (element) {
console.log(element)
})
/**
* Output:
* Lorem ipsum
* 9
* [ 'lorem ipsum dolor', true ]
* false
*/
}
run()
Needless to say, this pattern can be unnecessarily complex for lists without
multiple types of elements. Also, if there are too many types of elements in a
list, it can become difficult to manage too.
The key is to identify if you really need an iterator based on your list and its
future change possibilities. What’s more, the Iterator pattern is only useful in
lists, and lists can sometimes limit you to their linear mode of access. Other
data structures can sometimes give you greater performance benefits.
15. Mediator
Your application design may sometimes require you to play around with a
large number of distinct objects that house various kinds of business logic
and often depend on one another. Handling the dependencies can
sometimes get tricky as you need to keep track of how these objects
exchange data and control between them.
The Mediator design pattern is aimed at helping you solve this problem by
isolating the interaction logic for these objects into a separate object by
itself.
https://kinsta.com/blog/javascript-design-patterns/ 39/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// Reference to the manager, writer's name, and a busy flag that the
this.manager = manager
this.name = name
this.busy = false
this.finishWriting = function () {
if (this.busy === true) {
console.log(this.name + " finished writing \"" + this.assign
this.busy = false
return this.manager.notifyWritingComplete(this.assignment)
} else {
console.log(this.name + " is not writing any article")
}
}
}
// Reference to the manager, editor's name, and a busy flag that the
this.manager = manager
this.name = name
this.busy = false
https://kinsta.com/blog/javascript-design-patterns/ 40/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
}
this.finishEditing = function () {
if (this.busy === true) {
console.log(this.name + " finished editing \"" + this.assign
this.manager.notifyEditingComplete(this.assignment)
this.busy = false
} else {
console.log(this.name + " is not editing any article")
}
}
}
function run() {
// Create a manager
let manager = new Manager()
https://kinsta.com/blog/javascript-design-patterns/ 41/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// Create workers
let editors = [
new Editor("Ed", manager),
new Editor("Phil", manager),
]
let writers = [
new Writer("Michael", manager),
new Writer("Rick", manager),
]
/**
* Output:
* Michael started writing "var vs let in JavaScript"
* Rick started writing "JS promises"
*
* After 2s, output:
* Michael finished writing "var vs let in JavaScript"
* Ed started editing "var vs let in JavaScript"
* Rick finished writing "JS promises"
* Phil started editing "JS promises"
*
* After 3s, output:
* Ed finished editing "var vs let in JavaScript"
* "var vs let in JavaScript" is ready to publish
* Phil finished editing "JS promises"
* "JS promises" is ready to publish
*/
run()
While the mediator provides your app design with decoupling and a great
deal of flexibility, at the end of the day, it’s another class that you need to
maintain. You must assess whether your design can really benefit from a
mediator before writing one so you don’t end up adding unnecessary
complexity to your codebase.
https://kinsta.com/blog/javascript-design-patterns/ 42/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
It’s also important to keep in mind that even though the mediator class
doesn’t hold any direct business logic, it still contains a lot of code that is
crucial to the functioning of your app and can therefore quickly get pretty
complex.
16. Memento
Versioning objects is another common problem that you’ll face when
developing apps. There are a lot of use cases where you need to maintain
the history of an object, support easy rollbacks, and sometimes even
support reverting those rollbacks. Writing the logic for such apps can be
tough.
// The memento class that can hold one snapshot of the Originator class
function Text(contents) {
// Contents of the document
this.contents = contents
// The originator class that holds the latest version of the document
function Document(contents) {
// Holder for the memento, i.e., the text of the document
this.text = new Text(contents)
https://kinsta.com/blog/javascript-design-patterns/ 43/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// Function to save new contents as a memento
this.save = function (contents) {
this.text = new Text(contents)
return this.text
}
// The caretaker class that providers helper functions to modify the doc
function DocumentManager(document) {
// Holder for the originator, i.e., the document
this.document = document
// Add the initial state of the document as the first version of the
this.history.push(document.getText())
https://kinsta.com/blog/javascript-design-patterns/ 44/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
}
function run() {
// Create a document
let blogPost = new Document("")
https://kinsta.com/blog/javascript-design-patterns/ 45/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
run()
https://kinsta.com/blog/javascript-design-patterns/ 46/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
While the Memento design pattern is a great solution for managing the
history of an object, it can get very resource-intensive. Since each memento
is almost a copy of the object, it can bloat your app’s memory very quickly if
not used in moderation.
17. Observer
The Observer pattern provides an alternate solution to the multi-object-
interaction problem (seen before in the Mediator pattern).
// The newsletter class that can send out posts to its subscribers
function Newsletter() {
// Maintain a list of subscribers
this.subscribers = []
// The reader class that can subscribe to and receive updates from newsl
function Reader(name) {
this.name = name
this.receiveNewsletter = function(post) {
console.log("Newsletter received by " + name + "!: " + post)
}
function run() {
// Create two readers
let rick = new Reader("ed")
let morty = new Reader("morty")
https://kinsta.com/blog/javascript-design-patterns/ 48/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
run()
While the Observer pattern is a slick way of passing around control and data,
it is better suited to situations where there are a large number of senders
and receivers interacting with each other via a limited number of
connections. If the objects were to all make one-to-one connections, you
would lose the edge you get by publishing and subscribing to events since
there will always be only one subscriber for each publisher (when it would
have been better handled by a direct line of communication between
them).
18. State
The State design pattern is one of the most used design patterns across the
software development industry. Popular JavaScript frameworks like React
and Angular heavily rely on the State pattern to manage data and app
behavior based on that data.
Put simply, the State design pattern is helpful in situations where you can
define definitive states of an entity (which could be a component, a page, an
app, or a machine), and the entity has a predefined reaction to the state
change.
https://kinsta.com/blog/javascript-design-patterns/ 49/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Let’s say you’re trying to build a loan application process. Each step in the
application process can be defined as a state.
While the customer usually sees a small list of simplified states of their
application (pending, in review, accepted, and rejected), there can be other
steps involved internally. At each of these steps, the application will be
assigned to a distinct person and can have unique requirements.
Here’s how you can build a task management system using the State design
pattern:
// Create the task class with a title, assignee, and duration of the tas
function Task(title, assignee) {
this.title = title
this.assignee = assignee
switch (state) {
case STATE_TODO:
this.state = new TODO(this)
break
case STATE_IN_PROGRESS:
this.state = new IN_PROGRESS(this)
break
case STATE_READY_FOR_REVIEW:
this.state = new READY_FOR_REVIEW(this)
break
case STATE_DONE:
this.state = new DONE(this)
break
https://kinsta.com/blog/javascript-design-patterns/ 50/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
default:
return
}
// Invoke the callback function for the new state after it is se
this.state.onStateSet()
}
// TODO state
function TODO(task) {
this.onStateSet = function () {
console.log(task.assignee + " notified about new task \"" + task
}
}
// IN_PROGRESS state
function IN_PROGRESS(task) {
this.onStateSet = function () {
console.log(task.assignee + " started working on the task \"" +
}
}
this.onStateSet = function () {
task.setAssignee(this.getAssignee())
console.log(task.assignee + " notified about completed task \""
}
}
// DONE state that removes the assignee of the task since it is now comp
function DONE(task) {
this.getAssignee = function () {
return ""
}
this.onStateSet = function () {
task.setAssignee(this.getAssignee())
console.log("Task \"" + task.title + "\" completed")
}
}
function run() {
// Create a task
let task1 = new Task("Create a login page", "Developer 1")
https://kinsta.com/blog/javascript-design-patterns/ 51/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// Output: Developer 1 notified about new task "Create a login page"
// Set it to IN_PROGRESS
task1.updateState(STATE_IN_PROGRESS)
// Output: Developer 1 started working on the task "Create a login p
task1.updateState(STATE_DONE)
// Output: Task "Create a login page" completed
task2.updateState(STATE_DONE)
// Output: Task "Create an auth server" completed
run()
While the State pattern does a great job of segregating steps in a process, it
can become extremely difficult to maintain in large applications that have
multiple states.
On top of that, if your process design allows more than just linearly moving
through all the states, you’re in for writing and maintaining more code, since
each state transition needs to be handled separately.
19. Strategy
Also known as the Policy pattern, the Strategy pattern aims to help you
encapsulate and freely interchange classes using a common interface. This
https://kinsta.com/blog/javascript-design-patterns/ 52/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
helps maintain a loose coupling between the client and the classes and
allows you to add as many implementations as you’d like.
// set the website configuration for which each hosting provider woul
this.setConfiguration = function(configuration) {
this.configuration = configuration
}
// the generic estimate method that calls the provider's unique metho
this.estimateMonthlyCost = function() {
return this.provider.estimateMonthlyCost(this.configuration)
}
}
this.estimateMonthlyCost = function(configuration){
return configuration.duration * configuration.workloadSize * this
}
}
this.estimateMonthlyCost = function(configuration){
return configuration.duration / 60 * configuration.workloadSize *
}
}
https://kinsta.com/blog/javascript-design-patterns/ 53/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
// Baz Hosting assumes the average workload to be of 10 MB in size
function BazHosting (){
this.name = "BazHosting"
this.rate = 0.032
this.estimateMonthlyCost = function(configuration){
return configuration.duration * this.rate
}
}
function run() {
hostingProvider.setProvider(barHosting)
console.log("BarHosting cost: " + hostingProvider.estimateMonthlyCost
// Output: BarHosting cost: 2601.9840
hostingProvider.setProvider(bazHosting)
console.log("BarHosting cost: " + hostingProvider.estimateMonthlyCost
// Output: BarHosting cost: 2710.4000
run()
https://kinsta.com/blog/javascript-design-patterns/ 54/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Also, the encapsulation takes away finer details about each variant’s internal
logic, so your client is unaware of how a variant is going to behave.
20. Visitor
The Visitor pattern aims to help you make your code extensible.
The idea is to provide a method in the class that allows objects of other
classes to make changes to objects of the current class easily. The other
objects visit the current object (also called the place object), or the current
class accepts the visitor objects, and the place object handles the visit of
each external object appropriately.
// Visitor class that defines the methods to be called when visiting eac
function Reader(name, cash) {
this.name = name
this.cash = cash
// The visit methods can access the place object and invoke availabl
this.visitBookstore = function(bookstore) {
console.log(this.name + " visited the bookstore and bought a boo
bookstore.purchaseBook(this)
}
this.visitLibrary = function() {
console.log(this.name + " visited the library and read a book")
}
function run() {
// Create a reader (the visitor)
let reader = new Reader("Rick", 30)
run()
The only flaw in this design is that each visitor class needs to be updated
whenever a new place is added or modified. In cases where multiple visitors
and place objects exist together, this can be difficult to maintain.
Other than that, the method works great for enhancing the functionality of
classes dynamically.
Now that you’ve seen the most common design patterns across JavaScript,
here are some tips that you should keep in mind when implementing them.
There are many patterns that solve the same problem but take different
approaches and have different consequences. So your criteria for selecting
a design pattern shouldn’t just be whether it solves your problem or not — it
should also be how well it solves your problem and whether there is any
other pattern that can present a more efficient solution.
answer yes to the last question, design patterns might not be the most
optimal way of development for you.
Design patterns do not lead to heavy code reuse unless they are planned in
a very early stage of app design. Randomly using design patterns at various
stages can lead to an unnecessarily complex app architecture that you’d
have to spend weeks simplifying.
While it’s good to identify standard solutions and keep them in mind when
you encounter similar problems, there’s a good chance the new problem
you encountered will not fit the exact same description as an older problem.
In such a case, you might end up implementing a suboptimal solution and
wasting resources.
https://kinsta.com/blog/javascript-design-patterns/ 58/66
When Should You Use
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Design Patterns?
To sum up, here are a few cues that you should look out for to use design
patterns. Not all of them apply to every app’s development, but they should
give you a good idea of what to look out for when thinking of using design
patterns:
If a design pattern solves your problem and helps you write code that’s
simple, reusable, modular, loosely coupled, and free of “code smell,” it might
be the right way to go.
Another good tip to keep in mind is to avoid making everything about design
patterns. Design patterns are meant to help you solve problems. They are
not laws to abide by or rules to strictly follow. The ultimate rules and laws are
still the same: Keep your code clean, simple, readable, and scalable. If a
design pattern helps you do that while solving your problem, you should be
good to go with it.
Summary
https://kinsta.com/blog/javascript-design-patterns/ 59/66
12/8/23, 8:38 PM An Extensive Guide to JavaScript Design Patterns
Today, there are hundreds of design patterns available that will solve almost
any problem that you encounter while building apps. However, not every
design pattern will really solve your problem every time.
Just like any other programming convention, design patterns are meant to
be taken as suggestions for solving problems. They are not laws to be
followed all the time, and if you treat them like laws, you might end up doing
a lot of damage to your apps.
Once your app is finished, you’ll need a place to host it — and Kinsta’s
Application Hosting solutions are chief among the fastest, most reliable, and
most secure. You just need to sign in to your MyKinsta account (Kinsta’s
custom administrative dashboard), connect to your GitHub repository, and
launch! Plus, you’re only charged for the resources your app uses.
What are the design patterns that you regularly use in your software
programming job? Or is there a pattern that we missed in the list? Let us
know in the comments below!
Get all your applications, databases, and WordPress sites online and under
one roof. Our feature-packed, high-performance cloud platform includes:
Get started with a free trial of our Application Hosting or Database Hosting.
Explore our plans or talk to sales to find your best fit.
https://kinsta.com/blog/javascript-design-patterns/ 60/66