All topics
Patternsadvanced

Decorator Pattern in JavaScript

Wrapping a function or object to add extra behavior transparently, without modifying its original source code.

The Decorator pattern attaches additional behavior to an existing function or object dynamically, by wrapping it, without altering the original implementation's source code at all — the wrapped version presents (mostly) the same interface as the original but adds behavior before, after, or around the original call. In JavaScript, this pattern shows up constantly, both informally as simple higher-order 'wrapper' functions and formally through the newer, native @decorator syntax now standardized for classes.

The Decorator pattern is like a phone case: it wraps around the original phone, adding new capabilities (grip, protection, a kickstand) without opening up and modifying the phone's actual internal hardware — you can layer multiple cases/add-ons, and underneath it all, the original untouched phone is still doing exactly what it always did.

Key Concepts

1
The simplest, most common form is a higher-order function that takes a function and returns a new function wrapping it with extra behavior — logging every call, timing execution, retrying on failure, or memoizing results — while still ultimately delegating to (calling) the original function to do the real work. This requires no special language syntax at all, just closures and functions returning functions, and it's genuinely indistinguishable in practice from certain 'decorator' examples in more formally OOP-structured languages.
2
JavaScript also has an actual @decorator syntax (a relatively recent, now-standardized addition, building on earlier stage proposals that several transpilers and TypeScript supported for years before native standardization) specifically for classes and class members, letting you write @logged class Example {} or @readonly get value() {} to apply a decorator function that wraps or modifies the class/method/field definition itself at definition time, rather than manually wrapping calls yourself. This formal syntax is mostly used in framework and library code (dependency injection frameworks, ORMs defining schema via decorators, testing frameworks marking test methods) rather than typical application-level business logic.
@decorator@logged class Example {}@readonly get value() {}
3
The key distinction from Proxy-based interception, covered elsewhere, is that decorators typically wrap a *specific, known* function or method at definition time with a fixed set of additional behavior, while a Proxy intercepts *arbitrary* operations (property access, assignment, deletion) on an object dynamically at runtime — decorators are generally simpler and more common for the specific case of 'wrap this one function/method with extra behavior,' while Proxy is reserved for more general-purpose interception of an object's fundamental operations.