CodeBlanks Logo Icon CodeBlanks

The decorator pattern

JavaScript course · Expert · Lesson 40/63

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

  1. 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.

  2. 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;
      };
    }
  3. 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

All lessons in Expert