What is the Decorator pattern?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
The Decorator pattern wraps something to add behaviour, while keeping the same interface — so callers cannot tell the difference and nothing else has to change.
In JavaScript this usually means a function that takes a function and returns a new one:
function withLogging(fn) {
return (...args) => {
console.log(`calling ${fn.name}`, args);
const result = fn(...args);
console.log("returned", result);
return result;
};
}
const save = withLogging(saveUser); // same signature, extra behaviour
Why wrapping beats editing
The original function stays focused on its job. Logging, timing, caching, retrying and rate-limiting are all concerns you can add and remove independently, and stack in any order:
const fetchUser = withRetry(withCache(withLogging(rawFetchUser)));
Each layer is testable on its own, and removing one is deleting one call.
Async decorators are the common case
function withRetry(fn, attempts = 3) {
return async (...args) => {
for (let i = 0; i < attempts; i++) {
try { return await fn(...args); }
catch (err) { if (i === attempts - 1) throw err; }
}
};
}
Things to watch
The wrapper must preserve the contract: same arguments, same return type, this forwarded if the target is a method. Use fn.apply(this, args) rather than fn(...args) when decorating methods.
Stack traces get deeper and debugging harder, so keep wrappers thin and name them clearly.
A note on syntax
TypeScript's @decorator syntax and the JavaScript decorators proposal are a formalised version of this for classes. The plain function form above needs no build step and covers most cases — reach for the syntax when you are decorating class methods and want it declarative.
