![]() |
It's the height of the holiday season — and, as they say, it's time to decorate! But this time we're not talking about trees, lights, or packages: we're talking about how to decorate our JavaScript code to make it more elegant and modular.
No, we won't use the magic syntax @decorator (still not fully supported by all environments), but we'll see how to implement the same concept in pure JavaScript, in a simple and understandable way.
What does “decorate” mean in JavaScript
In programming, “decorating” an element means adding new functionality without modifying the original code.
A Decorator is therefore a function that takes another function, class, or method and wraps it with new behavior.
It's a very useful pattern for cleanly managing cross-cutting logic such as logging, security, caching, or Validation.
A first example: adding logs to methods
Imagine we want to know when a function is called and with which parameters.
With the Decorator pattern, we can write a function that “adds” this functionality to any method.
function logExecution(fn, name) {
return function (...args) {
console.log(`Calling ${name} with arguments:`, args);
const result = fn.apply(this, args);
console.log(`Result from ${name}:`, result);
return result;
};
}
class Calculator {
add(a, b) {
return a + b;
}
}
// Let's manually decorate the method
Calculator.prototype.add = logExecution(Calculator.prototype.add, "add");
const calc = new Calculator();
calc.add(2, 3);
👉 In this example, the logExecution function receives the original method, creates a "decorated" version that prints the logs, and then calls the original function.
The result? We've added automatic logging without changing the method's internal logic.
Decorate multiple methods with a single function
We can create a function that automatically applies a decorator to all methods in a class:
function decorateAll(target, decorator) {
const methodNames = Object.getOwnPropertyNames(target.prototype)
.filter(name => typeof target.prototype[name] === "function" && name !== "constructor");
for (const name of methodNames) {
const original = target.prototype[name];
target.prototype[name] = decorator(original, name);
}
}
// We use the same decorator as before
decorateAll(Calculator, logExecution);
const c = new Calculator();
c.add(5, 7);
With this technique, we can add common behaviors to all functions of a class with a single call—a “global” decoration, like putting a single garland on the entire tree 🎄.
A practical case: Access control in an API
A real-world example where decorators (even hand-implemented ones) can make a difference is in authentication management.
Imagine a controller that exposes several API methods, but only some need to be accessible to authenticated users. Instead of inserting repetitive checks into each method, we can createSee a Decorator to manage them for us:
function requireAuth(fn, name) {
return function (...args) {
if (!this.user || !this.user.isAuthenticated) {
throw new Error(`Access denied to ${name}: user not authenticated`);
}
return fn.apply(this, args);
};
}
class UserController {
constructor(user) {
this.user = user;
}
getUserProfile() {
return { name: this.user.name, email: this.user.email };
}
deleteAccount() {
return "Account deleted";
}
}
// We apply the decorator only to sensitive methods
UserController.prototype.getUserProfile =
requireAuth(UserController.prototype.getUserProfile, "getUserProfile");
UserController.prototype.deleteAccount =
requireAuth(UserController.prototype.deleteAccount, "deleteAccount");
const guest = new UserController({ isAuthenticated: false });
const admin = new UserController({ name: "Alice", email: "alice@example.com", isAuthenticated: true });
try {
guest.getUserProfile(); // Will throw error
} catch (e) {
console.error(e.message);
}
console.log(admin.getUserProfile()); // It works
With this approach, the code remains clean, secure, and reusable: the authentication logic is concentrated in a single place and can be applied to any method without repetition.
🎁 Conclusion
Just like Christmas decorations, decorators should be used wisely: they're not needed everywhere, but when used well, they make your code more elegant and modular.
And the great thing is that, even without new syntax or transpilers, we can get the same benefits with pure JavaScript, simply by combining functions and classes intelligently.
Happy coding... and happy holidays! 🎅✨
📚 Did you like Decorators?
If the concept of code decoration has intrigued you and you want to learn more about other techniques to make your JavaScript more elegant, modular and maintainable, then you might also find this article interesting: The Decorator Pattern in Modern JavaScript: Adding Functionality Without Breaking Code.
This is an in-depth look at other patterns and approaches that allow you to extend the behavior of your code without weighing it down, maintaining a clean and easily manageable structure. reusable.
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment
