Read the first two chapters Free here!!!
The JavaScript Handbook: Design Patterns is a comprehensive guide to understanding and mastering the essential design patterns that every developer needs. These patterns offer time-tested solutions to common programming challenges, making your code cleaner, more maintainable, and easier to scale.
This book is designed to take developers from foundational concepts to expert-level proficiency in JavaScript design patterns. You’ll learn how to implement and apply patterns like the Module, Singleton, Factory, Observer, and Prototype—all of which are essential for structuring and organizing modern applications.
What you will learn:
- Module Pattern: Encapsulate your code, maintain private variables, and avoid polluting the global namespace.
- Singleton Pattern: Ensure only one instance of an object exists, perfect for logging, configuration, and database connections.
- Factory Pattern: Streamline object creation, especially when working with multiple object types.
- Observer Pattern: Master event-driven programming, a key concept for UI development and reactive systems.
- Prototype Pattern: Reuse methods across objects and take control of inheritance in JavaScript.
Each chapter includes detailed explanations, multiple-choice quizzes, coding exercises, and full solutions. These exercises help solidify your understanding and give you the confidence to recognize coding challenges, identify which pattern to apply, and refactor your code for clarity and maintainability.
Who is this book for? This book is ideal for:
- Beginner developers who want a clear path to understanding design patterns.
- Experienced developers looking to sharpen their skills and write cleaner, more modular code.
- Web developers preparing for coding interviews, where knowledge of design patterns is essential.
Whether you’re looking to improve your problem-solving skills, prepare for interviews, or simply write better code, this book will guide you through everything you need to know about JavaScript Design Patterns. By the end, you’ll be able to spot and apply design patterns in your own applications, write cleaner code, and boost your development speed.
JavaScript Handbook JavaScript Design Patterns
JavaScript Patterns Unlocked: Module, Singleton, Factory, and More
By
Laurence Lars Svekis
Copyright © 2024 Laurence Lars Svekis All rights reserved.
Dedicated to
Alexis and Sebastian
Thank you for your support
For more content and to learn more, visit
JavaScript Handbook JavaScript Design Patterns
Summary
The JavaScript Handbook: Design Patterns offers a comprehensive, hands-on approach to learning the most essential design patterns for modern JavaScript development. Covering foundational patterns like Module, Singleton, Factory, Observer, and Prototype, this book provides everything you need to build clean, maintainable, and scalable applications.
Each pattern is explained in detail, starting with clear definitions and followed by hands-on coding exercises, real-world examples, and multiple-choice quizzes. These interactive elements enable readers to apply their knowledge immediately, ensuring a deeper understanding of each concept.
Key Takeaways:
- Learn the most essential design patterns to streamline your development process.
- Practice with real-world coding exercises that simulate practical development scenarios.
- Test your understanding with multiple-choice quizzes and step-by-step solutions.
This book is designed for beginners and experienced developers alike, providing a structured, interactive learning experience. By the end, readers will have the knowledge and skills to recognize coding problems, apply the correct design pattern, and write clean, efficient code.
Introduction
Welcome to the JavaScript Handbook: Design Patterns—your guide to mastering one of the most critical aspects of JavaScript development. This book is designed to help developers, from beginners to experienced programmers, understand and apply essential design patterns to build clean, maintainable, and scalable applications.
Design patterns offer proven solutions to common coding challenges, making your development process more efficient and your code more reliable. Through practical examples, interactive exercises, and multiple-choice quizzes, this book demystifies complex concepts like the Module, Singleton, Factory, Observer, and Prototype patterns, among others.
Each pattern is broken down with clear explanations and hands-on coding examples, allowing you to understand how, when, and why to use each pattern. With this knowledge, you’ll be able to recognize coding problems, identify the appropriate pattern, and confidently apply it to real-world projects.
Whether you’re preparing for a technical interview or seeking to become a more proficient JavaScript developer, this book will equip you with a toolkit of design patterns that will streamline your workflow and make you a more versatile coder.
Introduction to the Module Pattern in JavaScript
What is the Module Pattern?
The module pattern is a design pattern used in JavaScript to encapsulate private and public data and methods. Before the advent of modern JavaScript module systems (like ES Modules or CommonJS), developers often used the module pattern to create self-contained units of code that kept internal details private while exposing a public API.
Why Use the Module Pattern?
- Encapsulation of Code: It helps you group related variables, functions, and functionality into a single unit.
- Preventing Global Pollution: The module pattern keeps variables and functions out of the global namespace, preventing naming collisions and promoting cleaner code.
- Data Privacy: By leveraging closures, you can create private variables and methods that cannot be accessed from the outside.
- Public API Exposure: It provides a controlled way to expose certain parts of the code to the outside world (the “public” methods) while keeping other parts hidden (“private” details).
How Does It Work?
The module pattern typically uses an Immediately Invoked Function Expression (IIFE). An IIFE is a function that runs immediately after it is defined. The structure looks like this:
var myModule = (function() {
// Private variables and functions
var privateVar = “I am private”;
function privateMethod() {
console.log(“This is a private method!”);
}
// Public methods and variables
return {
publicVar: “I am public”,
publicMethod: function() {
console.log(“This is a public method!”);
console.log(“Accessing privateVar inside: ” + privateVar);
privateMethod();
}
};
})();
In the snippet above, myModule holds the returned object, which contains publicVar and publicMethod. The variables and functions defined inside the IIFE but not returned remain private and inaccessible from the outside.
Key Concepts
IIFE (Immediately Invoked Function Expression):
An IIFE looks like this:
(function() {
// code
})();
- The parentheses around the function definition ensure it’s treated as an expression, and the trailing () executes it immediately.
- Closures:
The module pattern relies on closures. Variables defined in an outer function remain accessible to inner functions even after the outer function finishes execution. In the module pattern, the IIFE returns an object that can still access private data through closures, thus “closing over” the local variables. - Private vs. Public Members:
- Private: Declared inside the IIFE but not returned.
- Public: Exposed as part of the returned object.
Benefits
- Maintainability: Code is easier to read, maintain, and reuse.
- Security: Private data is less prone to accidental modifications.
- Namespace Management: Reduces global variable usage and prevents naming conflicts.
Drawbacks
- Testing Private Members: Private functions and variables can be harder to test directly since they are hidden.
- Refactoring Complexity: Breaking the code into multiple smaller modules may become cumbersome if you rely too heavily on a single large module.
Detailed Code Examples
Example 1: Simple Counter Module
var counterModule = (function() {
var count = 0; // private variable
function increment() {
count++;
}
function decrement() {
count–;
}
return {
getCount: function() {
return count;
},
increase: function() {
increment();
console.log(“Count after increment: ” + count);
},
decrease: function() {
decrement();
console.log(“Count after decrement: ” + count);
}
};
})();
// Usage
counterModule.increase(); // Count after increment: 1
counterModule.increase(); // Count after increment: 2
counterModule.decrease(); // Count after decrement: 1
console.log(counterModule.getCount()); // 1
// console.log(count); // Error: count is not defined globally
Example 2: Revealing Module Pattern
The “Revealing Module Pattern” is a variant that explicitly maps private members to public pointers, making it clearer which members are exposed.
var calculatorModule = (function() {
var total = 0; // private
function add(num) {
total += num;
}
function subtract(num) {
total -= num;
}
function reset() {
total = 0;
}
function getTotal() {
return total;
}
// Reveal only the methods we want to make public
return {
add: add,
subtract: subtract,
reset: reset,
getTotal: getTotal
};
})();
calculatorModule.add(10);
calculatorModule.subtract(3);
console.log(calculatorModule.getTotal()); // 7
calculatorModule.reset();
console.log(calculatorModule.getTotal()); // 0
Example 3: Namespaced Modules
You can create multiple modules under a single namespace:
var app = app || {};
app.moduleA = (function() {
var privateMessage = “Hello from module A”;
return {
greet: function() {
console.log(privateMessage);
}
};
})();
app.moduleB = (function() {
var privateValue = 42;
return {
getValue: function() {
return privateValue;
}
};
})();
app.moduleA.greet(); // Hello from module A
console.log(app.moduleB.getValue()); // 42
Multiple-Choice Questions
- Which best describes the Module Pattern in JavaScript?
A. A pattern used to handle asynchronous code execution.
B. A design pattern for organizing code into reusable classes.
C. A pattern using closures and IIFEs to create private and public members.
D. A pattern that removes the need for objects entirely.
Answer: C.
The module pattern relies on IIFEs and closures to encapsulate variables and functions, resulting in private and public members. - What does IIFE stand for?
A. Immediately Invoked Function Expression
B. Internal Invoked Functional Execution
C. Important Invoked Function Executor
D. Immediately Integrated Function Expansion
Answer: A.
IIFE stands for Immediately Invoked Function Expression. - Which of the following is a characteristic of an IIFE?
A. It can never return a value.
B. It is executed immediately after it’s defined.
C. It must always be anonymous.
D. It cannot contain private variables.
Answer: B.
An IIFE executes immediately as soon as it is defined. - In the module pattern, what is the purpose of returning an object from the IIFE?
A. To export public methods and properties.
B. To ensure the function runs only once.
C. To allow recursion within the module.
D. To bind the module to the global window object.
Answer: A.
Returning an object exposes the public interface of the module. - What can be considered a major benefit of the module pattern?
A. Global scope pollution.
B. Easily accessible private variables from outside.
C. Encapsulation and avoiding namespace collisions.
D. It prevents closures.
Answer: C.
The module pattern encapsulates code and avoids polluting the global scope. - Which of these is NOT a disadvantage of the module pattern?
A. Testing private members is harder.
B. Potential complexity in refactoring large modules.
C. Might require more memory usage than global variables.
D. It prevents any public methods from being created.
Answer: D.
The module pattern does allow public methods; that’s the whole point of returning a public API.
In the following code, which is a private variable?
var myModule = (function() {
var secret = “hidden”;
return {
reveal: function() { return secret; }
};
})();
- A. myModule
B. secret
C. reveal
D. (function() { … })
Answer: B.
secret is a private variable, accessible only inside the IIFE. - Which of the following patterns is closely related to the module pattern and explicitly maps private members to public pointers?
A. Singleton Pattern
B. Constructor Pattern
C. Revealing Module Pattern
D. Prototype Pattern
Answer: C.
The Revealing Module Pattern explicitly returns an object literal mapping private members to public keys. - How do closures help in the module pattern?
A. By preventing variables from being garbage collected.
B. By allowing inner functions to remember the environment in which they were created.
C. By making all variables global.
D. By preventing IIFEs from running automatically.
Answer: B.
Closures allow functions to retain access to their parent scope’s variables, enabling private data. - Which is true about the module pattern and global variables?
A. It encourages the use of many global variables.
B. It completely eliminates global variables.
C. It reduces the need for global variables, but typically one global reference is made for the module.
D. It makes global variables automatically private.
Answer: C.
Usually, the module is assigned to a single global variable (e.g., var myModule = …), reducing global namespace pollution. - The module pattern is often used in older JS codebases because:
A. Modern bundlers and ES modules weren’t widely available in older environments.
B. The module pattern is newer than ES modules.
C. It is mandated by ECMAScript 6.
D. It doesn’t rely on closures.
Answer: A.
Before ES Modules and bundlers became common, the module pattern was a popular way to create modular code. - What happens if you do not return an object from the IIFE in the module pattern?
A. You can’t have private variables anymore.
B. You won’t have a public interface to access.
C. The IIFE won’t run.
D. An error is thrown at runtime.
Answer: B.
Without a returned object, no public methods or variables are exposed. - Which keyword is not required to create a module pattern using IIFE?
A. function
B. return
C. var or const to hold the module
D. class
Answer: D.
The module pattern does not require the class keyword.
Consider this code:
var moduleX = (function() {
var count = 0;
return {
inc: function() { count++; },
get: function() { return count; }
}
})();
moduleX.inc();
console.log(moduleX.get());
- What is logged to the console?
A. undefined
B. 0
C. 1
D. An error
Answer: C.
After one increment, count is 1, so console.log(moduleX.get()) prints 1. - What is a key reason to use the module pattern?
A. To immediately globalize every variable in the script.
B. To manage and maintain code structure and data privacy.
C. To replace all functions with classes.
D. To ensure that variables persist across page reloads.
Answer: B.
The module pattern helps in organizing code and keeping data private. - If you have a private function inside a module, how can you call it externally?
A. Directly by its name.
B. By exposing it through the returned object.
C. By calling window.privateFunctionName().
D. By converting it into an arrow function.
Answer: B.
To call it externally, you must expose it in the module’s returned object. - Which symbol pair typically wraps the IIFE function definition?
A. {}
B. ()
C. []
D. <>
Answer: B.
IIFEs are wrapped in () both around the function expression and at the end to invoke it. - In the module pattern, what does the returned object represent?
A. The private variables.
B. The public API of the module.
C. The entire global namespace.
D. Unused code.
Answer: B.
The returned object represents the public API that other code can use. - The module pattern was heavily used before ES6 modules because:
A. Browsers did not natively support module imports.
B. It was the only pattern that worked in ES6.
C. It is slower than ES modules.
D. It required TypeScript.
Answer: A.
Before ES6, browsers didn’t support a native module system, so module pattern was a workaround. - The IIFE in the module pattern is crucial because:
A. It ensures that private variables are created anew every time the module is called.
B. It prevents any variables from being created.
C. It ensures immediate execution and creation of a closure.
D. It forces the use of this keyword.
Answer: C.
The IIFE ensures the module’s code runs once and sets up closures to maintain private state.
10 Coding Exercises with Full Solutions and Explanations
Exercise 1: Basic Private and Public Methods
Task: Create a module mathModule that has a private variable pi = 3.14159 and two public methods: circumference(radius) and area(radius). circumference(radius) returns 2 * pi * radius, and area(radius) returns pi * radius * radius.
Solution:
var mathModule = (function() {
var pi = 3.14159;
function circumference(r) {
return 2 * pi * r;
}
function area(r) {
return pi * r * r;
}
return {
circumference: circumference,
area: area
};
})();
// Explanation:
// pi is private. circumference and area are returned publicly, using pi internally.
console.log(mathModule.circumference(10)); // 62.8318
console.log(mathModule.area(10)); // 314.159
Exercise 2: Counter with Reset
Task: Create a module counter with a private variable count. Expose three public methods: increment(), decrement(), and current() that returns the current count. Also add a reset() method to set count back to 0.
Solution:
var counter = (function() {
var count = 0;
function increment() { count++; }
function decrement() { count–; }
function reset() { count = 0; }
function current() { return count; }
return {
increment: increment,
decrement: decrement,
reset: reset,
current: current
};
})();
counter.increment();
counter.increment();
console.log(counter.current()); // 2
counter.reset();
console.log(counter.current()); // 0
Explanation:
The count variable is private. We provide a public API to manipulate and read it.
Exercise 3: Module with Private Helper Function
Task: Create a module stringModule that has a private function reverseString(str) and a public function isPalindrome(str) that uses reverseString to determine if a string is a palindrome.
Solution:
var stringModule = (function() {
function reverseString(str) {
return str.split(”).reverse().join(”);
}
function isPalindrome(str) {
return str === reverseString(str);
}
return {
isPalindrome: isPalindrome
};
})();
console.log(stringModule.isPalindrome(“level”)); // true
console.log(stringModule.isPalindrome(“house”)); // false
Explanation:
reverseString is private and not accessible outside. isPalindrome uses it internally.
Exercise 4: Config Module
Task: Create a configModule that stores private configuration (like apiKey and apiUrl) and exposes methods getApiKey() and getApiUrl() to retrieve them.
Solution:
var configModule = (function() {
var apiKey = “12345-abcde”;
var apiUrl = “https://api.example.com”;
function getApiKey() { return apiKey; }
function getApiUrl() { return apiUrl; }
return {
getApiKey: getApiKey,
getApiUrl: getApiUrl
};
})();
console.log(configModule.getApiKey()); // “12345-abcde”
console.log(configModule.getApiUrl()); // “https://api.example.com”
Explanation:
Sensitive data like apiKey and apiUrl remain private. Only getters are exposed.
Exercise 5: Todo List Module
Task: Create a todoModule that stores a private array of todos. Public methods: add(todo), remove(index), and list() that returns all todos.
Solution:
var todoModule = (function() {
var todos = [];
function add(todo) {
todos.push(todo);
}
function remove(index) {
if (index >= 0 && index < todos.length) {
todos.splice(index, 1);
}
}
function list() {
return todos.slice(); // return a copy
}
return {
add: add,
remove: remove,
list: list
};
})();
todoModule.add(“Learn JavaScript”);
todoModule.add(“Write Code”);
console.log(todoModule.list()); // [“Learn JavaScript”, “Write Code”]
todoModule.remove(0);
console.log(todoModule.list()); // [“Write Code”]
Explanation:
The todos array is not accessible externally, ensuring controlled manipulation.
Exercise 6: Bank Account Module
Task: Create a bankAccount module with a private balance variable. Public methods: deposit(amount), withdraw(amount), and getBalance().
Solution:
var bankAccount = (function() {
var balance = 0;
function deposit(amount) {
if (amount > 0) balance += amount;
}
function withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
function getBalance() {
return balance;
}
return {
deposit: deposit,
withdraw: withdraw,
getBalance: getBalance
};
})();
bankAccount.deposit(100);
bankAccount.withdraw(30);
console.log(bankAccount.getBalance()); // 70
Explanation:
Balance remains private to prevent unauthorized changes directly.
Exercise 7: Settings Module with Default Values
Task: Create a module settingsModule that has private variables for theme = “dark” and language = “en”. Public methods: setTheme(theme), getTheme(), setLanguage(lang), and getLanguage().
Solution:
var settingsModule = (function() {
var theme = “dark”;
var language = “en”;
function setTheme(t) { theme = t; }
function getTheme() { return theme; }
function setLanguage(l) { language = l; }
function getLanguage() { return language; }
return {
setTheme: setTheme,
getTheme: getTheme,
setLanguage: setLanguage,
getLanguage: getLanguage
};
})();
settingsModule.setTheme(“light”);
console.log(settingsModule.getTheme()); // “light”
console.log(settingsModule.getLanguage()); // “en”
Explanation:
The module encapsulates settings and provides controlled getters/setters.
Exercise 8: Memoization Module
Task: Create a fibModule that computes the nth Fibonacci number. Use a private cache object to store computed values. Public method: fib(n) that returns the nth Fibonacci number using memoization.
Solution:
var fibModule = (function() {
var cache = {};
function fib(n) {
if (n < 2) return n;
if (cache[n]) return cache[n];
cache[n] = fib(n – 1) + fib(n – 2);
return cache[n];
}
return {
fib: fib
};
})();
console.log(fibModule.fib(10)); // 55
Explanation:
cache is private. Each computed Fibonacci number is stored, making subsequent calls faster.
Exercise 9: User Authentication Module
Task: Create authModule that stores a private isLoggedIn boolean and username. Public methods: login(user), logout(), and status() that returns the username if logged in, otherwise “Not logged in”.
Solution:
var authModule = (function() {
var isLoggedIn = false;
var username = “”;
function login(user) {
isLoggedIn = true;
username = user;
}
function logout() {
isLoggedIn = false;
username = “”;
}
function status() {
return isLoggedIn ? “Logged in as ” + username : “Not logged in”;
}
return {
login: login,
logout: logout,
status: status
};
})();
authModule.login(“Alice”);
console.log(authModule.status()); // “Logged in as Alice”
authModule.logout();
console.log(authModule.status()); // “Not logged in”
Explanation:
isLoggedIn and username are private, preventing external code from tampering directly.
Exercise 10: Timer Module
Task: Create a timerModule that has a private variable startTime. Public methods: start(), stop(), which returns the elapsed time in milliseconds, and reset() which resets the timer.
Solution:
var timerModule = (function() {
var startTime = null;
function start() {
startTime = Date.now();
}
function stop() {
if (startTime === null) {
return 0;
}
var elapsed = Date.now() – startTime;
startTime = null;
return elapsed;
}
function reset() {
startTime = null;
}
return {
start: start,
stop: stop,
reset: reset
};
})();
timerModule.start();
setTimeout(function() {
var elapsed = timerModule.stop();
console.log(“Elapsed time: ” + elapsed + ” ms”);
}, 500);
Explanation:
startTime is private, and the public methods handle timing operations securely.
Conclusion
The Module Pattern in JavaScript is a powerful, pre-ES6 approach to structuring code, promoting encapsulation, protecting the global namespace, and clearly separating public APIs from private implementation details. By understanding and practicing with the pattern, developers gain insight into closures, scoping, and code organization techniques that remain useful, even in the era of modern ES modules.
Introduction to the Singleton Pattern in JavaScript
What is the Singleton Pattern?
The Singleton pattern is a design pattern that ensures a class has only one instance, while providing a global point of access to that instance. In JavaScript, which is a prototype-based language rather than a classical OOP language, you don’t have “classes” in the traditional sense (at least pre-ES6), but you can still implement singletons.
Why Use the Singleton Pattern?
- Single Point of Access: Having one and only one instance can be beneficial. For example, consider a configuration object or a database connection manager where multiple instances might cause conflicts.
- Global State Management: Singletons can be used to store global state in a controlled manner.
- Resource Management: Some resources, like caching objects or logging systems, might need a single consolidated instance.
Characteristics of the Singleton Pattern
- Single Instance: Only one instance of the object is created.
- Lazy Initialization: The instance is often created only when first requested.
- Global Access Point: The same instance is accessible throughout the codebase without the need to recreate it.
Implementing Singletons in JavaScript
Before ES6 modules and classes, singletons were often created using IIFEs or objects that store a single instance. In modern JavaScript, one can use classes and static methods or leverage closures.
Classic Singleton using Object Literals
var mySingleton = {
property: “I’m the only instance!”,
method: function() {
console.log(“This method belongs to a singleton instance.”);
}
};
mySingleton.method(); // “This method belongs to a singleton instance.”
While this approach creates a single instance, it doesn’t “enforce” a singleton since you could create another similar object. However, mySingleton itself is just one object.
Singleton with Closure
var singleton = (function() {
var instance; // private variable to hold the instance
function createInstance() {
var obj = { name: “SingletonInstance”, timestamp: Date.now() };
return obj;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
var instance1 = singleton.getInstance();
var instance2 = singleton.getInstance();
console.log(instance1 === instance2); // true
Here, the singleton is an IIFE returning an object with a getInstance method. On the first call, it creates the instance. Subsequent calls return the same instance, ensuring only one instance exists.
ES6 Class-based Singleton
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.data = “Some data”;
Singleton.instance = this;
}
getData() {
return this.data;
}
}
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true
In this ES6 class example, we store the instance as a static property Singleton.instance. Subsequent attempts to create a new instance return the previously created instance, preserving the singleton property.
Advantages of the Singleton Pattern
- Controlled Access to a Single Instance: Centralized management of certain resources or states.
- Consistency: Ensures that the global state remains consistent and not fragmented into multiple instances.
- Configuration Management: Useful when you have app-wide configuration or services that should be unified.
Disadvantages of the Singleton Pattern
- Global State: Singletons might encourage the use of global state, making the code harder to reason about.
- Testing Difficulties: Singletons can be harder to test because they maintain state across the entire application lifetime.
- Tight Coupling: Code that depends on singletons might be harder to refactor since multiple parts of the application rely on that single instance.
Multiple Code Examples
Example 1: Simple Singleton with an Object
var logger = {
logs: [],
log: function(message) {
this.logs.push(message);
console.log(“Log added: ” + message);
}
};
logger.log(“First log”);
logger.log(“Second log”);
// logger is a single object used across the codebase
Example 2: Singleton with Lazy Instantiation
var dbConnection = (function() {
var instance;
function createConnection() {
return {
id: Math.random(),
status: ‘connected’
};
}
return {
getConnection: function() {
if (!instance) {
instance = createConnection();
}
return instance;
}
};
})();
var c1 = dbConnection.getConnection();
var c2 = dbConnection.getConnection();
console.log(c1.id === c2.id); // true
Example 3: Singleton in ES6 with a Symbol
const INSTANCE = Symbol(‘instance’);
class Configuration {
constructor() {
if (Configuration[INSTANCE]) {
return Configuration[INSTANCE];
}
this.config = { env: ‘production’, version: ‘1.0.0’ };
Configuration[INSTANCE] = this;
}
get(key) {
return this.config[key];
}
set(key, value) {
this.config[key] = value;
}
}
let config1 = new Configuration();
let config2 = new Configuration();
console.log(config1 === config2); // true
Multiple Choice Questions
- What does the Singleton pattern ensure?
A. Multiple instances of a class.
B. Only one instance of a class.
C. No instances of a class.
D. One instance per method.
Answer: B.
The Singleton pattern ensures only one instance is created. - How is a Singleton typically implemented in JavaScript?
A. By creating a global variable for each instance.
B. By using closures or static properties to store a single instance.
C. By removing constructors from classes.
D. By using arrow functions only.
Answer: B.
Common approaches involve closures or class static properties to ensure a single instance. - Which is a benefit of the Singleton pattern?
A. It guarantees an object has only one instance.
B. It allows multiple instances to be created easily.
C. It reduces the need for memory management.
D. It prohibits global state.
Answer: A.
The Singleton pattern ensures a single instance, which can be beneficial in certain scenarios. - Which of these is a downside of using a Singleton pattern?
A. It simplifies testing.
B. It prevents global state.
C. It can make testing harder due to shared state.
D. It ensures multiple instances always exist.
Answer: C.
Since a singleton holds global state, it can complicate testing. - What is a common use-case for a Singleton?
A. For every user-created object.
B. Database connections or configuration objects.
C. For array manipulation utilities.
D. For styling CSS dynamically.
Answer: B.
Singletons are often used for managing database connections, config objects, or loggers. - In the classic JavaScript implementation using closures, how is the instance stored?
A. As a global variable.
B. As a static property on the function.
C. In a local variable inside an IIFE (closure).
D. Inside the prototype property.
Answer: C.
The instance is often stored in a variable inside an IIFE, leveraging closures. - Which pattern ensures that a class only has one instance and provides a global point of access to it?
A. Factory Pattern
B. Singleton Pattern
C. Observer Pattern
D. Strategy Pattern
Answer: B.
The Singleton pattern is the correct choice. - When the Singleton instance is created only when it is needed, this is known as:
A. Early instantiation.
B. Forced instantiation.
C. Lazy instantiation.
D. Over instantiation.
Answer: C.
Creating the instance only upon the first request is lazy instantiation. - Which keyword in ES6 can be used to define a class from which we can create a Singleton?
A. class
B. function
C. var
D. singleton
Answer: A.
The class keyword can be used to define classes in ES6, which can then be adapted for Singleton logic. - In an ES6 Singleton, what prevents multiple instances from being created?
A. A conditional check in the constructor that returns an existing instance if present.
B. The static keyword alone.
C. Using this keyword only once.
D. The new keyword stops working.
Answer: A.
The constructor checks if an instance already exists and returns it if so. - Which of the following is not necessarily true for a Singleton?
A. It must be implemented using a class.
B. It has only one instance.
C. It provides a global access point to that instance.
D. It may use lazy instantiation.
Answer: A.
Singletons can be implemented without classes (e.g., using closures or object literals). - What does “providing a global point of access” mean in the context of Singletons?
A. The instance must be attached to the window object.
B. You have a single method or variable that returns the instance everywhere.
C. It can only be accessed from a global event.
D. It must be defined in the global scope.
Answer: B.
A global point of access means you have a known method or variable to get that single instance anywhere in the code. - If a Singleton object stores internal state, what is a potential problem?
A. It reduces the size of the code.
B. The internal state might be changed by multiple parts of the code, causing unexpected issues.
C. It improves security.
D. It never leads to bugs.
Answer: B.
Shared state can be altered by various parts of the code, leading to unpredictable behavior. - In a testing environment, why can Singletons be problematic?
A. They are always easy to mock.
B. They can’t be accessed in tests.
C. They hold persistent state, making tests order-dependent and harder to isolate.
D. They automatically reset their state before each test.
Answer: C.
Persistent state in singletons can cause test interference and order-dependence. - Which pattern often leads to a global variable-like structure?
A. Singleton Pattern
B. Adapter Pattern
C. Decorator Pattern
D. Module Pattern
Answer: A.
The Singleton can act like a globally accessible instance, similar to a global variable. - Can we create multiple Singletons in an application for different purposes?
A. No, the term “Singleton” means only one singleton per application.
B. Yes, we can have multiple distinct singletons for different responsibilities.
C. Only if we use ES6 classes.
D. Only in Node.js.
Answer: B.
You can have multiple different singleton implementations, each ensuring a single instance of a particular kind of object. - If you need to ensure only one instance of a logger object, how do you enforce it in JavaScript?
A. By using a closure or static property and returning the same instance every time.
B. By never calling the constructor.
C. By using Object.freeze().
D. By throwing an error if the object is duplicated.
Answer: A.
You ensure a single instance by checking if an instance exists and returning it rather than creating a new one. - Which is a sign that you might need a Singleton?
A. You find yourself creating the same object multiple times unnecessarily.
B. You want many copies of the same object.
C. You want to stop using global objects.
D. You do not want any state at all.
Answer: A.
If you keep recreating the same object and need only one, a singleton might be appropriate. - How does the Singleton pattern relate to modular code design?
A. It’s unrelated and cannot be used in modular code.
B. It can be used in combination with modules to provide a single instance of a resource module-wide.
C. It forces all modules to have their own singleton.
D. It breaks modular design.
Answer: B.
Singletons can be combined with modules to ensure a single instance per module or application. - Is the Singleton pattern considered a creational pattern?
A. No, it is a behavioral pattern.
B. No, it is a structural pattern.
C. Yes, it is a creational pattern.
D. It’s not a recognized pattern.
Answer: C.
The Singleton pattern is considered a creational pattern because it deals with object creation.
10 Coding Exercises with Full Solutions and Explanations
Exercise 1: Simple Singleton Getter
Task: Implement a singleton that returns an object with a random ID. Confirm that subsequent calls return the same object.
Solution:
var simpleSingleton = (function() {
var instance;
function createInstance() {
return { id: Math.random() };
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
var s1 = simpleSingleton.getInstance();
var s2 = simpleSingleton.getInstance();
console.log(s1 === s2); // true
Explanation:
The closure stores a single instance. Once created, the same instance is returned.
Exercise 2: Logging Singleton
Task: Create a singleton logger that keeps an array of messages. Provide a log(message) method and a getLogs() method. Ensure all code gets the same instance.
Solution:
var logger = (function() {
var instance;
function createLogger() {
var logs = [];
return {
log: function(msg) {
logs.push(msg);
},
getLogs: function() {
return logs.slice();
}
};
}
return {
getInstance: function() {
if (!instance) {
instance = createLogger();
}
return instance;
}
};
})();
var logger1 = logger.getInstance();
logger1.log(“First log”);
var logger2 = logger.getInstance();
logger2.log(“Second log”);
console.log(logger1.getLogs()); // [“First log”, “Second log”]
Explanation:
Both logger1 and logger2 point to the same instance, sharing state.
Exercise 3: ES6 Class Singleton
Task: Create an ES6 class Config that only allows one instance. Add get(key) and set(key, value) methods. Demonstrate that multiple new Config() calls return the same instance.
Solution:
class Config {
constructor() {
if (Config.instance) {
return Config.instance;
}
this.settings = {};
Config.instance = this;
}
set(key, value) {
this.settings[key] = value;
}
get(key) {
return this.settings[key];
}
}
const conf1 = new Config();
conf1.set(“mode”, “production”);
const conf2 = new Config();
console.log(conf2.get(“mode”)); // “production”
console.log(conf1 === conf2); // true
Explanation:
Config.instance ensures conf1 and conf2 point to the same object.
Exercise 4: Database Connection Singleton
Task: Simulate a database connection as a singleton. The connect() method returns the same connection object every time, storing a connectedAt timestamp.
Solution:
var dbSingleton = (function() {
var connection;
function connect() {
return {
connectedAt: new Date()
};
}
return {
getConnection: function() {
if (!connection) {
connection = connect();
}
return connection;
}
};
})();
var dbc1 = dbSingleton.getConnection();
var dbc2 = dbSingleton.getConnection();
console.log(dbc1 === dbc2); // true
Explanation:
The connection variable is private. Once set, it stays the same.
Exercise 5: Cache Singleton
Task: Implement a cache singleton with set(key, value) and get(key). Show that retrieving the cache from different parts of the code returns the same data.
Solution:
var cacheSingleton = (function() {
var cacheData = {};
return {
set: function(key, value) {
cacheData[key] = value;
},
get: function(key) {
return cacheData[key];
}
};
})();
cacheSingleton.set(“token”, “abc123”);
console.log(cacheSingleton.get(“token”)); // “abc123”
Explanation:
The module returns a single object. You can’t create another instance of cacheSingleton.
Exercise 6: Singleton with Initialization Flag
Task: Create a singleton that has an init() method that sets a certain property. Subsequent calls to init() should not overwrite if already initialized.
Solution:
var initSingleton = (function() {
var instance;
function createInstance() {
return { initialized: false, value: null };
}
return {
getInstance: function() {
if (!instance) instance = createInstance();
return instance;
},
init: function(val) {
var inst = this.getInstance();
if (!inst.initialized) {
inst.initialized = true;
inst.value = val;
}
}
};
})();
initSingleton.init(“Start”);
console.log(initSingleton.getInstance().value); // “Start”
initSingleton.init(“Another”);
console.log(initSingleton.getInstance().value); // still “Start”
Explanation:
We control initialization logic in the singleton to prevent multiple re-initializations.
Exercise 7: Singleton Counter
Task: Create a singleton counter with increment(), decrement(), and getCount() methods.
Solution:
var counterSingleton = (function() {
var instance;
function createCounter() {
var count = 0;
return {
increment: function() { count++; },
decrement: function() { count–; },
getCount: function() { return count; }
};
}
return {
getInstance: function() {
if (!instance) instance = createCounter();
return instance;
}
};
})();
var c1 = counterSingleton.getInstance();
c1.increment();
c1.increment();
var c2 = counterSingleton.getInstance();
c2.decrement();
console.log(c1.getCount()); // 1 (same instance)
Explanation:
c1 and c2 share the same internal counter state.
Exercise 8: Singleton with Configuration Loading
Task: Create a singleton that “loads” some configuration from a mock object only once.
Solution:
var configLoaderSingleton = (function() {
var instance;
function loadConfig() {
return { apiKey: “XYZ”, env: “dev” };
}
return {
getConfig: function() {
if (!instance) {
instance = loadConfig();
}
return instance;
}
};
})();
console.log(configLoaderSingleton.getConfig()); // {apiKey: “XYZ”, env: “dev”}
Explanation:
The config is loaded only once, subsequent calls return the cached config object.
Exercise 9: Singleton for Localization
Task: Create a singleton that stores a language setting. Add methods setLanguage(lang) and getLanguage().
Solution:
var localizationSingleton = (function() {
var language = “en”;
return {
setLanguage: function(lang) {
language = lang;
},
getLanguage: function() {
return language;
}
};
})();
console.log(localizationSingleton.getLanguage()); // “en”
localizationSingleton.setLanguage(“fr”);
console.log(localizationSingleton.getLanguage()); // “fr”
Explanation:
Since we never return a constructor, this is just a single object. It’s effectively a singleton.
Exercise 10: Singleton with Reset Method
Task: Create a singleton that maintains a sessionID. Provide getSession() to retrieve it, and resetSession() to create a new session.
Solution:
var sessionSingleton = (function() {
var sessionID;
function createSession() {
return “session-” + Math.random();
}
return {
getSession: function() {
if (!sessionID) {
sessionID = createSession();
}
return sessionID;
},
resetSession: function() {
sessionID = createSession();
}
};
})();
console.log(sessionSingleton.getSession()); // e.g. “session-0.12345”
sessionSingleton.resetSession();
console.log(sessionSingleton.getSession()); // new session ID
Explanation:
We maintain a private sessionID. resetSession() creates a new unique session ID, but still through the singleton interface.
Conclusion
The Singleton pattern in JavaScript provides a way to ensure only one instance of a particular object exists. This can simplify state management and resource handling. However, it should be used judiciously since it can lead to global state management challenges and complicate testing.
By understanding how to implement singletons—whether with IIFEs, closures, or ES6 classes—you can apply this pattern where appropriate, such as for configuration management, logging utilities, or database connections. The code examples, questions, and exercises above will help you deepen your understanding and mastery of the Singleton pattern in JavaScript.
