
- Javascript Basics Tutorial
- Javascript - Home
- JavaScript - Roadmap
- JavaScript - Overview
- JavaScript - Features
- JavaScript - Enabling
- JavaScript - Placement
- JavaScript - Syntax
- JavaScript - Hello World
- JavaScript - Console.log()
- JavaScript - Comments
- JavaScript - Variables
- JavaScript - let Statement
- JavaScript - Constants
- JavaScript - Data Types
- JavaScript - Type Conversions
- JavaScript - Strict Mode
- JavaScript - Reserved Keywords
- JavaScript Operators
- JavaScript - Operators
- JavaScript - Arithmetic Operators
- JavaScript - Comparison Operators
- JavaScript - Logical Operators
- JavaScript - Bitwise Operators
- JavaScript - Assignment Operators
- JavaScript - Conditional Operators
- JavaScript - typeof Operator
- JavaScript - Nullish Coalescing Operator
- JavaScript - Safe Assignment Operator
- JavaScript - Delete Operator
- JavaScript - Comma Operator
- JavaScript - Grouping Operator
- JavaScript - Yield Operator
- JavaScript - Spread Operator
- JavaScript - Exponentiation Operator
- JavaScript - Operator Precedence
- JavaScript Control Flow
- JavaScript - If...Else
- JavaScript - While Loop
- JavaScript - For Loop
- JavaScript - For...in
- Javascript - For...of
- JavaScript - Loop Control
- JavaScript - Break Statement
- JavaScript - Continue Statement
- JavaScript - Switch Case
- JavaScript - User Defined Iterators
- JavaScript Functions
- JavaScript - Functions
- JavaScript - Function Expressions
- JavaScript - Function Parameters
- JavaScript - Default Parameters
- JavaScript - Function() Constructor
- JavaScript - Function Hoisting
- JavaScript - Self-Invoking Functions
- JavaScript - Arrow Functions
- JavaScript - Function Invocation
- JavaScript - Function call()
- JavaScript - Function apply()
- JavaScript - Function bind()
- JavaScript - Closures
- JavaScript - Variable Scope
- JavaScript - Global Variables
- JavaScript - Smart Function Parameters
- JavaScript Objects
- JavaScript - Number
- JavaScript - Boolean
- JavaScript - Strings
- JavaScript - Arrays
- JavaScript - Date
- JavaScript - DataView
- JavaScript - Handler
- JavaScript - Math
- JavaScript - RegExp
- JavaScript - Symbol
- JavaScript - Sets
- JavaScript - WeakSet
- JavaScript - Maps
- JavaScript - WeakMap
- JavaScript - Iterables
- JavaScript - Reflect
- JavaScript - TypedArray
- JavaScript - Template Literals
- JavaScript - Tagged Templates
- Object Oriented JavaScript
- JavaScript - Objects
- JavaScript - Classes
- JavaScript - Object Properties
- JavaScript - Object Methods
- JavaScript - Static Methods
- JavaScript - Display Objects
- JavaScript - Object Accessors
- JavaScript - Object Constructors
- JavaScript - Native Prototypes
- JavaScript - ES5 Object Methods
- JavaScript - Encapsulation
- JavaScript - Inheritance
- JavaScript - Abstraction
- JavaScript - Polymorphism
- JavaScript - Destructuring
- JavaScript - Destructuring Assignment
- JavaScript - Object Destructuring
- JavaScript - Array Destructuring
- JavaScript - Nested Destructuring
- JavaScript - Optional Chaining
- JavaScript - Global Object
- JavaScript - Mixins
- JavaScript - Proxies
- JavaScript Versions
- JavaScript - History
- JavaScript - Versions
- JavaScript - ES5
- JavaScript - ES6
- ECMAScript 2016
- ECMAScript 2017
- ECMAScript 2018
- ECMAScript 2019
- ECMAScript 2020
- ECMAScript 2021
- ECMAScript 2022
- JavaScript Asynchronous
- JavaScript - Asynchronous
- JavaScript - Callback Functions
- JavaScript - Promises
- JavaScript - Async/Await
- JavaScript - Microtasks
- JavaScript - Promisification
- JavaScript - Promises Chaining
- JavaScript - Timing Events
- JavaScript - setTimeout()
- JavaScript - setInterval()
- JavaScript Cookies
- JavaScript - Cookies
- JavaScript - Cookie Attributes
- JavaScript - Deleting Cookies
- JavaScript Browser BOM
- JavaScript - Browser Object Model
- JavaScript - Window Object
- JavaScript - Document Object
- JavaScript - Screen Object
- JavaScript - History Object
- JavaScript - Navigator Object
- JavaScript - Location Object
- JavaScript - Console Object
- JavaScript Web APIs
- JavaScript - Web API
- JavaScript - History API
- JavaScript - Storage API
- JavaScript - Forms API
- JavaScript - Worker API
- JavaScript - Fetch API
- JavaScript - Geolocation API
- JavaScript Events
- JavaScript - Events
- JavaScript - DOM Events
- JavaScript - addEventListener()
- JavaScript - Mouse Events
- JavaScript - Keyboard Events
- JavaScript - Form Events
- JavaScript - Window/Document Events
- JavaScript - Event Delegation
- JavaScript - Event Bubbling
- JavaScript - Event Capturing
- JavaScript - Custom Events
- JavaScript Error Handling
- JavaScript - Error Handling
- JavaScript - try...catch
- JavaScript - Debugging
- JavaScript - Custom Errors
- JavaScript - Extending Errors
- JavaScript Important Keywords
- JavaScript - this Keyword
- JavaScript - void Keyword
- JavaScript - new Keyword
- JavaScript - var Keyword
- JavaScript HTML DOM
- JavaScript - HTML DOM
- JavaScript - DOM Methods & Properties
- JavaScript - DOM Document
- JavaScript - DOM Elements
- JavaScript - DOM Attributes (Attr)
- JavaScript - DOM Forms
- JavaScript - Changing HTML
- JavaScript - Changing CSS
- JavaScript - DOM Animation
- JavaScript - DOM Navigation
- JavaScript - DOM Collections
- JavaScript - DOM NodeList
- JavaScript - DOM DOMTokenList
- JavaScript Advanced Chapters
- JavaScript - Bubble Sort Algorithm
- JavaScript - Circular Reference Error
- JavaScript - Code Testing with Jest
- JavaScript - CORS Handling
- JavaScript - Data Analysis
- JavaScript - Dead Zone
- JavaScript - Design Patterns
- JavaScript - Engine and Runtime
- JavaScript - Execution Context
- JavaScript - Function Composition
- JavaScript - Immutability
- JavaScript - Kaboom.js
- JavaScript - Lexical Scope
- JavaScript - Local Storage
- JavaScript - Memoization
- JavaScript - Minifying JS
- JavaScript - Mutability vs Immutability
- JavaScript - Package Manager
- JavaScript - Parse S-Expressions
- JavaScript - Prototypal Inheritance
- JavaScript - Reactivity
- JavaScript - Require Function
- JavaScript - Selection API
- JavaScript - Session Storage
- JavaScript - SQL CRUD Operations
- JavaScript - Supercharged Sorts
- JavaScript - Temporal Dead Zone
- JavaScript - Throttling
- JavaScript - TRPC Library
- JavaScript - Truthy and Falsy Values
- JavaScript - Upload Files
- JavaScript - Date Comparison
- JavaScript - Recursion
- JavaScript - Data Structures
- JavaScript - Base64 Encoding
- JavaScript - Callback Function
- JavaScript - Current Date/Time
- JavaScript - Date Validation
- JavaScript - Filter Method
- JavaScript - Generating Colors
- JavaScript - HTTP Requests
- JavaScript - Insertion Sort
- JavaScript - Lazy Loading
- JavaScript - Linked List
- JavaScript - Nested Loop
- JavaScript - Null Checking
- JavaScript - Get Current URL
- JavaScript - Graph Algorithms
- JavaScript - Higher Order Functions
- JavaScript - Empty String Check
- JavaScript - Form Handling
- JavaScript - Functional Programming
- JavaScript - Parameters vs Arguments
- JavaScript - Prototype
- JavaScript - Reactive Programming
- JavaScript - Reduce Method
- JavaScript - Rest Operator
- JavaScript - Short Circuiting
- JavaScript - Undefined Check
- JavaScript - Unit Testing
- JavaScript - Validate URL
- JavaScript Miscellaneous
- JavaScript - Ajax
- JavaScript - Async Iteration
- JavaScript - Atomics Objects
- JavaScript - Rest Parameter
- JavaScript - Page Redirect
- JavaScript - Dialog Boxes
- JavaScript - Page Printing
- JavaScript - Validations
- JavaScript - Animation
- JavaScript - Multimedia
- JavaScript - Image Map
- JavaScript - Browsers
- JavaScript - JSON
- JavaScript - Multiline Strings
- JavaScript - Date Formats
- JavaScript - Get Date Methods
- JavaScript - Set Date Methods
- JavaScript - Modules
- JavaScript - Dynamic Imports
- JavaScript - BigInt
- JavaScript - Blob
- JavaScript - Unicode
- JavaScript - Shallow Copy
- JavaScript - Call Stack
- JavaScript - Reference Type
- JavaScript - IndexedDB
- JavaScript - Clickjacking Attack
- JavaScript - Currying
- JavaScript - Graphics
- JavaScript - Canvas
- JavaScript - Debouncing
- JavaScript - Performance
- JavaScript - Style Guide
JavaScript - Design Patterns
In JavaScript, design patterns are classes and communicating objects that are designed to deal with a general design problem in a specific setting. Generic, reusable solutions to typical issues that come up throughout software design and development are known as software design patterns.
They give developers a forum to discuss successful design concepts and function as best practices for resolving particular kinds of problems.
What are Design Patterns?
A design pattern in software engineering is a generic, replicable fix for a typical problem in program design. The design is not complete enough to be coded right away. It is a description or model for problem-solving that can be used in various contexts.
Types of Software Design Patterns
In JavaScript, there are primarily three categories of design patterns namely −
- Creational Design Patterns
- Structural Design Patterns
- Behavioral Design Patterns
Let's discuss them one by one −
Creational Design Patterns in JavaScript
In software development, creational design patterns are a subset of design patterns. They work on the object generation process, trying to make it more flexible and efficient. It maintains the independence of the system and the composition, representation, and creation of its objects.
Singleton Pattern
The singleton design pattern makes sure a class has only one immutable instance. In simple terms, the singleton pattern is an object that cannot be changed or replicated. It is often useful when we want an immutable single point of truth for our application.
Suppose that we want to have a single object that has all of our application's configuration. We also want to make it prohibit to copy or modify that object.
Two ways to implement this pattern are using classes and object literals −
const Settings = { initialize: () => console.log('Application is now running'), refresh: () => console.log('Application data has been refreshed'), } // Freeze the object to prevent modifications Object.freeze(Settings) // "Application is now running" Settings.initialize() // "Application data has been refreshed" Settings.refresh() // Trying to add a new key Settings.version = "1.0" console.log(Settings)
This will generate the below result −
Application is now running Application data has been refreshed { initialize: [Function: initialize], refresh: [Function: refresh] }
class Settings { constructor() {} launch() { console.log('Application is now running') } refresh() { console.log('Application data has been refreshed') } } const appInstance = new Settings() Object.freeze(appInstance) // "Application is now running" appInstance.launch() // "Application data has been refreshed" appInstance.refresh()
Output
Here is the outcome of the above code −
Application is now running Application data has been refreshed
Factory Method Pattern
The Factory method pattern provides an interface for creating objects that can be modified after they are generated. By combining the logic for building our objects in a single place, this reduces and enhances the organization of our code.
This often used pattern can be implemented in two ways: by using classes or factory functions, which are methods that return an object.
class Creature { constructor(name, message) { this.name = name this.message = message this.type = "creature" } fly = () => console.log("Whoooosh!!") speak = () => console.log(this.message) } const creature1 = new Creature("Zee", "Hello! I'm Zee from outer space!") console.log(creature1.name) // output: "Zee"
This will produce the below output −
Zee
Abstract Factory Pattern
Without knowing the precise types of linked objects, we can use the Abstract Factory design to generate groups of them. This is useful when we need to build unique objects that share only a few characteristics.
It works like this: a primary abstract factory connects with the client, or user. This abstract factory then uses some logic to invoke a specific factory to generate the actual object. As a result, it acts as an overlay over the typical factory layout, allowing us to produce a large range of items in a single primary factory.
For example, we are able to develop a system for an automobile manufacturer that includes trucks, motorcycles, and cars. The Abstract Factory pattern, which handles several object types with a single primary factory, makes this easy for us to do.
// Each class represents a specific type of vehicle class Car { constructor () { this.type = "Car" this.wheels = 4 } startEngine = () => console.log("Vroom Vroom!") } class Truck { constructor () { this.type = "Truck" this.wheels = 8 } startEngine = () => console.log("Rumble Rumble!") } class Motorcycle { constructor () { this.type = "Motorcycle" this.wheels = 2 } startEngine = () => console.log("Zoom Zoom!") } const vehicleFactory = { createVehicle: function (vehicleType) { switch (vehicleType) { case "car": return new Car() case "truck": return new Truck() case "motorcycle": return new Motorcycle() default: return null } } } const car = vehicleFactory.createVehicle("car") const truck = vehicleFactory.createVehicle("truck") const motorcycle = vehicleFactory.createVehicle("motorcycle") console.log(car.type) car.startEngine() console.log(truck.type) truck.startEngine() console.log(motorcycle.type) motorcycle.startEngine()
Output
This will generate the below result −
Car Vroom Vroom! Truck Rumble Rumble! Motorcycle Zoom Zoom!
Builder Pattern
The Builder pattern is used to generate objects in "steps". Functions or methods that add particular attributes or methods are typically included in our object.
Because it separates the development of methods and attributes into separate entities, this design is cool.
The object we create will always contain every property and method defined by a class or factory function. Using the builder pattern, which enables us to construct an object and apply only the "steps" that are required, provides a more flexible approach.
// Define our creatures const creature1 = { name: "Buzz Lightwing", message: "You'll never debug me!" } const creature2 = { name: "Sneaky Bugsworth", message: "Can't catch me! Ha ha!" } // These functions add abilities to an object const enableFlying = obj => { obj.fly = () => console.log(`${obj.name} has taken flight!`) } const enableSpeaking = obj => { obj.speak = () => console.log(`${obj.name} says: "${obj.message}"`) } // Add abilities to the creatures enableFlying(creature1) creature1.fly() enableSpeaking(creature2) creature2.speak()
Output
This will give the following result −
Buzz Lightwing has taken flight! Sneaky Bugsworth says: "Can't catch me! Ha ha!"
Prototype Pattern
By using another object as a blueprint and inheriting its properties and methods, you can create an object using the prototype pattern.
Prototypal inheritance and how JavaScript handles it are likely familiar to you if you have been using JavaScript for a time.
As methods and properties can be shared between objects without depend on the same class, the final result is much more flexible than what we obtain when we use classes.
// Define a prototype object const creatureAbilities = { attack: () => console.log("Zap! Zap!"), escape: () => console.log("Soaring through the sky!") } // Create a new creature const creature1 = { name: "Winged Buzz", message: "You can't debug me!" } // Set the prototype Object.setPrototypeOf(creature1, creatureAbilities) // Confirm that the prototype is set successfully console.log(Object.getPrototypeOf(creature1)) console.log(creature1.message) console.log(creature1.attack()) console.log(creature1.escape())
Output
This will produce the following outcome −
{ attack: [Function: attack], escape: [Function: escape] } You can't debug me! Zap! Zap! undefined Soaring through the sky! undefined
Structural Design Patterns in JavaScript
In software development, structural design patterns are a subset of design patterns that focus on how classes or objects are put together to create complex, larger structures. In order to increase a software system's flexibility, re-usability, and maintainability, they help in the organization and management of relationships between objects.
Adapter Pattern
The Adapter Method is a structural design pattern which helps you to connect the gap between two incompatible interfaces and make them operate together.
Let us see an example below −
// Array of cities with population in millions const citiesPopulationInMillions = [ { city: "Mumbai", population: 12.5 }, { city: "Delhi", population: 19.0 }, { city: "Bangalore", population: 8.4 }, { city: "Kolkata", population: 4.5 }, ] // The new city we want to add const Chennai = { city: "Chennai", population: 7000000 } const toMillionsAdapter = city => { city.population = parseFloat((city.population / 1000000).toFixed(1)) } toMillionsAdapter(Chennai) // We add the new city to the array citiesPopulationInMillions.push(Chennai) // This function returns the largest population number const CityWithMostPopulation = () => { return Math.max(...citiesPopulationInMillions.map(city => city.population)) } console.log(CityWithMostPopulation())
Output
This will generate the below result −
19
Decorator Pattern
The decorator method is a structural design pattern that enables the addition of static or dynamic functionality to particular objects without changing how other objects of the same class behave.
Following is the simple demonstration of this Reactivity Pattern −
// Base class for a Car class Car { constructor() { this.description = "Basic Car"; } getDescription() { return this.description; } cost() { // Base price return 10000; } } // Decorator to add air conditioning class AirConditioningDecorator { constructor(car) { this.car = car; } getDescription() { return this.car.getDescription() + " + Air Conditioning"; } cost() { // Adds cost for air conditioning return this.car.cost() + 1500; } } // Decorator to add a sunroof class SunroofDecorator { constructor(car) { this.car = car; } getDescription() { return this.car.getDescription() + " + Sunroof"; } cost() { return this.car.cost() + 2000; } } // Create a basic car const myCar = new Car(); console.log(myCar.getDescription()); console.log(`Cost: $${myCar.cost()}`); // Add air conditioning feature const myCarWithAC = new AirConditioningDecorator(myCar); console.log(myCarWithAC.getDescription()); console.log(`Cost: $${myCarWithAC.cost()}`); // Add sunroof feature to the car const myCarWithACAndSunroof = new SunroofDecorator(myCarWithAC); console.log(myCarWithACAndSunroof.getDescription()); console.log(`Cost: $${myCarWithACAndSunroof.cost()}`);
Output
This will generate the below result −
Basic Car Cost: $10000 Basic Car + Air Conditioning Cost: $11500 Basic Car + Air Conditioning + Sunroof Cost: $13500
Facade Pattern
The Facade Method is a structural design pattern that improves client interaction with a subsystem by offering a higher-level, simplified interface to a group of interfaces within that subsystem.
To make it simpler to interface with a subsystem containing multiple complex parts in an automobile, we want to create a CarFacade class. The client should only use the CarFacade class; it should not use the individual components.
// Subsystem classes class Engine { start() { console.log("Engine started!"); } stop() { console.log("Engine stopped!"); } } class Transmission { shiftGear() { console.log("Transmission shifted!"); } } class AirConditioning { turnOn() { console.log("Air conditioning is ON."); } turnOff() { console.log("Air conditioning is OFF."); } } // Facade class class CarFacade { constructor() { this.engine = new Engine(); this.transmission = new Transmission(); this.airConditioning = new AirConditioning(); } startCar() { console.log("Starting the car..."); this.engine.start(); this.transmission.shiftGear(); this.airConditioning.turnOn(); } stopCar() { console.log("Stopping the car..."); this.airConditioning.turnOff(); this.transmission.shiftGear(); this.engine.stop(); } } // Client code using the Facade pattern const myCar = new CarFacade(); // Starting the car myCar.startCar(); // Stopping the car myCar.stopCar();
Output
This will generate the below result −
Starting the car... Engine started! Transmission shifted! Air conditioning is ON. Stopping the car... Air conditioning is OFF. Transmission shifted! Engine stopped!
Proxy Pattern
The Proxy Method is a structural design pattern that allows you to create an object substitute that can control access to the real object or serve as an intermediate.
// Real object class RealImage { constructor(filename) { this.filename = filename; this.load(); } load() { console.log(`Loading image: ${this.filename}`); } display() { console.log(`Displaying image: ${this.filename}`); } } // Proxy class class ProxyImage { constructor(filename) { this.filename = filename; // Real image is only loaded when needed this.realImage = null; } load() { if (!this.realImage) { // Lazy load this.realImage = new RealImage(this.filename); } } display() { // Ensure the real image is loaded before displaying it this.load(); this.realImage.display(); } } // Client code const image1 = new ProxyImage("image1.jpg"); const image2 = new ProxyImage("image2.jpg"); // Image is not loaded yet image1.display(); // Image is already loaded image1.display(); // A different image is loaded image2.display();
Output
This will lead to the below result −
Loading image: image1.jpg Displaying image: image1.jpg Displaying image: image1.jpg Loading image: image2.jpg Displaying image: image2.jpg
Behavioral Design Patterns in JavaScript
Behavioral design patterns are a subset of software development design patterns that focus on the interactions and communication between classes and objects. They focus on how classes and objects work together and communicate in order to fulfill responsibilities.
Chain of Responsibility Pattern
The Chain of Responsibility is used to route requests via a number of handlers. Each handler decides whether to process the request or pass it on to the next handler in the chain.
We could use the same example as before for this pattern because middlewares in Express are handlers that either execute a request or forward it to the next handler.
Iterator Pattern
The iterator can be used to navigate over the elements of a collection. This can appear unimportant in today's computer languages, but it used to be that way.
Any of the built-in JavaScript functions that let us iterate across data structures (for, forEach, for...of, for...in, map, reduce, filter, and so on) can use the iterator pattern.
As with any traversal technique, we write code to iterate through data with advanced structures such as trees or graphs.
Observer Pattern
The observer pattern allows you to create a subscription mechanism that notifies multiple items about any changes to the item being observed. Basically, the rule is like having an event listener on a specific object, and when that object does the action we are looking for, we do something.
React's useEffect hook could be an excellent example here. UseEffect executes a provided function at the time it is declared.
The hook is separated into two parts: the executable function and an array of dependents. If the array is empty, like in the example below, the function is called every time the component is shown.
useEffect(() => { console.log('The component has been rendered') }, [])
If we declare any variables in the dependency array, the function will only run when they change.
useEffect(() => { console.log('var1 has been changed') }, [var1])
Even simple JavaScript event listeners can be considered as observers. This approach is also illustrated by reactive programming and tools like RxJS, which manage asynchronous information and events across systems.