The decorator pattern
Learn to add behavior by wrapping a function with another function.
The decorator pattern
The decorator pattern adds behavior by wrapping a function with another function. You do not change the original; you write a wrapper that calls it.
The typical flow: function decorate(fn) { return function(...args) { /* before */ const result = fn.apply(this, args); /* after */ return result; }; }. The wrapper takes the same parameters and returns the same result, with extra behavior around it.
Logging, caching, counting and timing are added this way. You can chain several decorators on the same function.
Before and after
Decorators split behavior into before and after. The before part handles input validation, logging or counting. The after part handles result transformation, cache writing or error handling.
The split is decided by the position of the fn.apply(this, args) call. Code before it runs first, code after it runs last. fn.apply forwards the this context and all arguments to the original, so the decorator works with any function.
Exercise A logging decorator
Define a decorator function logCall(fn). Inside it, return function(...args) as the wrapper. The wrapper first logs with console.log('calling', fn.name), then calls with const result = fn.apply(this, args), logs the result with console.log('result', result) and return result. Define an add(a, b) function (returns a + b). Wrap with const wrapped = logCall(add) and call console.log(wrapped(2, 3)).
Show example solution
function logCall(fn) {
return function(...args) {
console.log('calling', fn.name);
const result = fn.apply(this, args);
console.log('result', result);
return result;
};
}
function add(a, b) {
return a + b;
}
const wrapped = logCall(add);
console.log(wrapped(2, 3));
Test yourself
-
What does a decorator return?
- A new function with the same signature
- It modifies the original function
- A number
- A Promise
Show answer
A new function with the same signature — Correct! A decorator returns a function with the same parameters and return as the original.
-
Call inside the wrapper
function logCall(fn) { return function(...args) { console.log('calling'); const result = ____; return result; }; }Show answer
function logCall(fn) { return function(...args) { console.log('calling'); const result = fn.apply(this, args); return result; }; } -
Match each use case to its behavior
- Logging
- Write call and result
- Caching
- Store result, return again
- Counter
- How many times called
- Timing
- Measure call duration