Suggest an editImprove this articleRefine the answer for “What is Module design pattern?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Module pattern** uses an IIFE and a closure to create private state and return a public API object. Variables inside the IIFE cannot be accessed from outside. ```js const Mod = (function() { let secret = 0; return { get() { return secret; } }; })(); Mod.secret; // undefined ``` **Key:** private state lives in a closure, not on the returned object.Shown above the full answer for quick recall.Answer (EN)Image**Module pattern** is a design pattern that wraps code in an IIFE to create a private scope and returns an object as a public API. ## Theory ### TL;DR - Wrap code in an [IIFE](/questions/iife) (immediately invoked function expression) to get a private scope - Variables declared inside never leak out - The returned object is the public API - only what you return is accessible - Before ES6 modules existed, this was the standard way to avoid polluting the global scope - You still see it in legacy codebases and wherever a singleton with private state is needed ### Quick example ```javascript const Counter = (function() { // count is private - nothing outside can reach it let count = 0; return { increment() { count++; }, decrement() { count--; }, getCount() { return count; } }; })(); Counter.increment(); Counter.increment(); console.log(Counter.getCount()); // 2 console.log(Counter.count); // undefined - truly private ``` The IIFE runs once, creates a [closure](/questions/closure) over `count`, then disappears. What remains is only the returned object. `count` lives in memory but is completely unreachable from outside. ### How it works Two things make this work: the IIFE and the closure. The IIFE runs immediately and drops the function reference after execution. But the inner functions `increment`, `decrement`, and `getCount` still hold a reference to the scope where `count` lives. That is a closure. As long as those functions exist, `count` stays in memory and is accessible only through them. This is not fake privacy. `Counter.count` returns `undefined` because there is no property called `count` on the object. The actual variable sits in a scope with no name - unreachable by any other code. ### Revealing Module Pattern A useful variation is the Revealing Module Pattern. Instead of defining methods inside the returned object, you define everything as private first, then selectively expose references. ```javascript const UserStore = (function() { let users = []; function add(user) { users.push(user); } function getAll() { return [...users]; // return a copy, not the original reference } function count() { return users.length; } // Reveal only what's needed return { add, getAll, count }; })(); UserStore.add({ id: 1, name: 'Alice' }); console.log(UserStore.getAll()); // [{ id: 1, name: 'Alice' }] ``` All logic is defined the same way. The `return` statement becomes a clear manifest of the public API - everything above it is private. ### When to use - You need a singleton with private internal state - You are working in a pre-ES6 environment or writing a script without a bundler - You want to wrap a library or SDK and expose only a clean interface - You need a self-contained script that cannot touch the global scope ### Module pattern vs ES6 modules [ES6 modules](/questions/es6-modules) (`import`/`export`) solve the same problem at the language level. An ES6 module file has its own scope by default - no IIFE needed. Anything not exported is private automatically. ```javascript // ES6 module - userStore.js let users = []; export function add(user) { users.push(user); } export function getAll() { return [...users]; } ``` The Module pattern still makes sense when everything lives in one file without a bundler, when building a script-tag library, or when you need a factory that creates multiple independent instances. ### Common mistakes **Returning a reference instead of a copy** Most bugs I have seen with this pattern come from exposing a direct reference to an internal array or object. ```javascript const Store = (function() { let items = []; return { getItems() { return items; }, // wrong - exposes the reference getItemsSafe() { return [...items]; } // right - returns a copy }; })(); const ref = Store.getItems(); ref.push('injected'); // you just mutated the private state ``` Return a copy. Always. **The Revealing Module Pattern trap** If a public method calls a private method, and you later reassign the public method from outside, the internal calls still point to the original private function. The override does not propagate inward. **Using the pattern when ES6 modules are available** In any modern project with a bundler, use `import`/`export`. The Module pattern is a workaround for a missing language feature. In a React or Node.js project it adds complexity without benefit. ### Where you see it - jQuery wrapped the entire library in a single `$` namespace using this pattern - Older browser SDKs (Google Analytics, legacy Stripe.js) use it to avoid touching the global object - Node packages written before CommonJS used variations of this approach - Interview questions reference it often because it tests closure knowledge directly ### Follow-up questions **Q:** What is an IIFE and why does the Module pattern need one? **A:** An IIFE is a function that runs immediately after being defined. The Module pattern uses it to create a temporary private scope. Once the IIFE finishes, the outer scope is gone, but the closures it created still hold references to the inner variables. **Q:** What happens if two modules need to share private state? **A:** They cannot, by design. Each IIFE gets its own scope. If you need shared state between modules, expose it through public API methods or move it to a third module that both use. **Q:** How does the Module pattern differ from a class with private fields (`#field`)? **A:** A class creates a prototype chain and supports multiple instances via `new`. The Module pattern in IIFE form typically produces a singleton. ES2022 private fields (`#`) are the class-based equivalent of what the Module pattern achieves with closures - but with better tooling support and clearer syntax. **Q:** Can you create multiple instances with the Module pattern? **A:** Yes. Drop the IIFE and use a factory function. Each call creates a fresh closure with independent state. ```javascript function makeCounter() { let count = 0; return { increment() { count++; }, getCount() { return count; } }; } const c1 = makeCounter(); const c2 = makeCounter(); // independent instance, separate count ``` **Q:** What does the interviewer actually want to hear when they ask about Module pattern? **A:** That you know it is built on closures, that "private" here is not a language keyword but an inaccessible scope, and that you know when ES6 modules are the better choice. ## Examples ### Basic counter ```javascript const Counter = (function() { let count = 0; return { increment() { count++; }, reset() { count = 0; }, getCount() { return count; } }; })(); Counter.increment(); Counter.increment(); console.log(Counter.getCount()); // 2 Counter.count = 999; // no effect on the internal variable console.log(Counter.getCount()); // still 2 ``` `Counter.count = 999` creates a new property on the returned object. The internal `count` variable is untouched - it lives in a different scope entirely. ### API client with private config ```javascript const ApiClient = (function() { const BASE_URL = 'https://api.example.com'; let authToken = null; function buildHeaders() { return { 'Content-Type': 'application/json', ...(authToken && { Authorization: `Bearer ${authToken}` }) }; } return { setToken(token) { authToken = token; }, async get(path) { const response = await fetch(`${BASE_URL}${path}`, { headers: buildHeaders() }); return response.json(); }, async post(path, body) { const response = await fetch(`${BASE_URL}${path}`, { method: 'POST', headers: buildHeaders(), body: JSON.stringify(body) }); return response.json(); } }; })(); ApiClient.setToken('my-secret-token'); ApiClient.get('/users'); // BASE_URL and authToken are inaccessible from outside ``` `BASE_URL` and `authToken` are locked inside the closure. External code can call `setToken` to update the token but cannot read it directly. ### Factory for multiple instances ```javascript function createFormValidator(rules) { const errors = {}; let submitted = false; function validate(data) { Object.keys(rules).forEach(field => { if (rules[field].required && !data[field]) { errors[field] = `${field} is required`; } }); return Object.keys(errors).length === 0; } return { validate, submit(data) { if (validate(data)) { submitted = true; return true; } return false; }, getErrors() { return { ...errors }; }, isSubmitted() { return submitted; } }; } const loginValidator = createFormValidator({ email: { required: true }, password: { required: true } }); const signupValidator = createFormValidator({ email: { required: true }, username: { required: true }, password: { required: true } }); // Each validator has completely independent state loginValidator.submit({ email: 'a@b.com' }); // fails: password missing console.log(signupValidator.isSubmitted()); // false - unaffected ``` Each call to `createFormValidator` produces a new closure with its own `errors` and `submitted`. The two validators share no state.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.