The revealing module pattern wraps private state and behaviour inside a function (often an IIFE), then returns a plain object whose properties are the public API. Private helpers stay as inner functions or variables; only what you attach to that returned object is visible to callers.
It is one variant of the classic module pattern from pre-ES2015 JavaScript,
when files did not have native import / export.
Shape
const counter = (function () {
let count = 0 // private
function increment() {
count += 1
}
function getCount() {
return count
}
return {
increment,
getCount,
}
})()
counter.increment()
console.log(counter.getCount()) // 1The “revealing” part is naming: you define real functions (increment,
getCount) inside the closure, then expose them as properties on the
returned object. Callers never see count; they only use the methods you
listed.
Why use it
- Encapsulation — Private data is not a property on the returned object, so it cannot be read or overwritten from outside without going through your API.
- Stable surface — The returned object is a small, explicit list of capabilities (sometimes called a facade).
- Refactoring room — You can change how
incrementworks internally without changing property names on the public object.
Compared to ES modules
Today, a file can achieve similar separation with top-level private symbols
and explicit export:
let count = 0
export function increment() {
count += 1
}
export function getCount() {
return count
}The revealing module pattern remains useful when you need a factory (many independent instances), when bundling legacy scripts without a module loader, or when teaching how closures and object literals compose into a module-like API.
Trade-offs
- Testing — Private functions are hard to unit-test in isolation; you test through the public API or accept that internals are covered indirectly.
- Memory — Each IIFE invocation creates new function instances; for many
instances, consider whether a
classor shared prototype fits better.