What are Decorators and How are they used in JavaScript?
Last Updated :
25 Oct, 2024
Decorators in JavaScript are a design pattern that enhances an object's behavior by wrapping it with additional functionality, without altering its original code. It enable modifications like logging or security checks without changing the underlying implementation. It adopted from languages like Python and C#, allows for flexible extension of object functionality in JavaScript applications.

How to Use Decorators in JavaScript?
A decorator is a higher-order function that modifies the behavior of a function, method, or class. Decorators are used for extending the functionality of a function, method, or class without modifying its code.
Syntax
let variable = function(object) {
object.property = 'characteristic';
}
// Use as decorator
@variable
class GFG
{ }
console.log(GFG.property);
Example: It is an example of decorators in older JavaScript versions. The `add` function accepts `print` as a parameter, where `print` acts as a decorator appending "is best" to the string "GFG". By using `add`, we extend and invoke `print`, effectively concatenating the strings.
JavaScript
// Working of Decorators in javascript
// "add" function takes the function as
// a parameter for wrapping function
// "print" is wrapped
function add(fn) {
return function (s) {
var gg = s + ' is Best';
// By concatenating we extend
// the function "add"
fn(gg);
}
}
// Decorated function
function print(s) {
console.log(s);
}
// Calling "add"
var g = add(print);
g('GFG');
Output
GFG is Best
Steps to Run Decorators in JavaScript
To run the decorators in JavaScript, we need to configure babel in out projec. Below is a step-by-step guide
Step 1: Initialize a Node App
npm init
Step 2: Install Babel and Required Plugins
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-proposal-decorators
The updated dependencies after installing above pacakges are:
"devDependencies": {
"@babel/cli": "^7.25.7",
"@babel/core": "^7.25.8",
"@babel/plugin-proposal-decorators": "^7.25.7",
"@babel/preset-env": "^7.25.8"
}
Step 3: Configure .babelrc file
// Filename - .babelrc
{
"plugins": [
["@babel/plugin-proposal-decorators", { "version": "legacy" }]
],
"presets": ["@babel/preset-env"]
}
Step 4: Define Decorator in index.js file
// Filename - index.js
let variable = function (target) {
target.property = 'GFG is best';
}
// Decorator
@variable
class GFG { }
// Print in the console
console.log(GFG.property);
Step 5: Compile the code using Babel
npx babel index.js --out-file compiled_output.js
This command will output the compiled file which will look like this:
JavaScript
// Filename - compiled_output.js
"use strict";
function _typeof(o) {
"@babel/helpers - typeof";
return (_typeof = "function" == typeof Symbol &&
"symbol" == typeof Symbol.iterator
? function (o) { return typeof o; }
: function (o) {
return o &&
"function" == typeof Symbol &&
o.constructor === Symbol &&
o !== Symbol.prototype
? "symbol"
: typeof o;
}), _typeof(o);
}
var _class;
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
(o.enumerable = o.enumerable || !1),
(o.configurable = !0), "value" in o &&
(o.writable = !0), Object.defineProperty(
e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), t &&
_defineProperties(e, t), Object.defineProperty(e, "prototype", {
writable: !1
}), e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _classCallCheck(a, n) {
if (!(a instanceof n))
throw new TypeError("Cannot call a class as a function");
}
// Filename - index.js
var variable = function variable(target) {
target.property = "GFG is best";
};
// Decorator
var GFG = variable(
(_class = /*#__PURE__*/ _createClass(function GFG() {
_classCallCheck(this, GFG);
}))
) || _class; // Print in the console
console.log(GFG.property);
Step 6: Run the output file
node compiled_output.js
The below example illustrates the decorators in JavaScript:
Example 1: Defining a decorator variable that adds a property to the class GFG, which is then accessed to log "GFG is best" in the console.
JavaScript
let variable = function (target) {
target.property = 'GFG is best';
}
// Decorator
@variable
class GFG { }
// Print in the console
console.log(GFG.property);
Output
GFG is best
Example 2: Defining a decorator factory variable that sets a class property based on the passed argument, assigning "GFG is Green" to GFG.property and logging it.
JavaScript
let variable = function (color) {
return function (target) {
target.property = color;
}
};
// The value is passed in the decorator
@variable('GFG is Green')
class GFG { }
console.log(GFG.property);
Output
GFG is Green
Why to uses Decorator ?
- Decorators allow for decorating code in an efficient and understandable way.
- Applying the same techniques to different parts of the code or wrapping functions manually can be challenging.
- In ES6, decorators help resolve these difficulties by providing a clear syntax for wrapping code with functions or additional logic.
- Decorators offer a standardized approach to applying wrappers around functions or other code blocks.
- Although JavaScript decorators are not yet directly supported, future versions may include native support for them.
Types of Decorators
Decorators are called by the appropriate details of the item which will be decorated. Decorators are actually functions that return another function. There are two types of decorators are supported now:
- Class member decorators
- Class Decorators
Class Member Decorators
These decorators are applied to a single member of a class. This decorator has properties, methods, getters, and setters. This decorator accepts 3 parameters:
- target: The class to which the member belongs to.
- name: Name of the class member.
- descriptor: Description of the member which is the object that is passed to Object.defineProperty.
Example 1: This example defines a method decorator gfg that logs the parameters and result of the add method in the geek class.
JavaScript
// Decorator function
function gfg(target, name, descriptor) {
let fn = descriptor.value;
// Checks if "descriptor.value" is a function
if (typeof fn == 'function') {
descriptor.value = function(...args) {
console.log(`parameters: ${args}`);
let result = fn.apply(this, args);
// Print the addition of passed arguments
console.log(`addition: ${result}`);
return result;
}
}
return descriptor;
}
class geek {
@gfg
add(a, b) {
return a + b;
}
}
let e = new geek();
e.add(100, 200);
Output
parameters: 100, 200
addition: 300
Example 2: This example demonstrates a readonly decorator to make the getColor method of the car class non-writable, preventing it from being overridden.
JavaScript
let readonly = function(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
class car {
constructor(color) {
this.color = color;
}
// Decorator
@readonly
getColor() {
return this.color;
}
}
const rCar = new car('car is Black');
// When descriptor.writable = false;
rCar.getColor = function() {
// When descriptor.writable = true;
return 'car is not Black'
}
console.log(rCar.getColor());
Output
car is Black
Class Decorators
These decorators are applied to the entire class. These functions are called with a single parameter which is to be decorated. This function is a constructor function. These decorators are not applied to each instance of the class, it only applies to the constructor function. These decorators are less useful than class member decorators. Because we can use a simple function to do everything which can be done by these decorators.
Example: This example demonstrates using a log
decorator to log the parameters passed to the gfg
class constructor.
JavaScript
function log() {
// Decorator function
return function decorator(Class) {
// "arrow" function
return (...args) => {
console.log(`Parameters: ${args}`);
return new Class(...args);
};
};
}
// Decorators
@log
class gfg {
constructor(name, category) {
this.name = name;
this.category = category;
}
}
const e = new gfg('geek', 'code');
// Arguments for Demo: args
console.log(e);
Output
(...args) =>
{
console.log(`Parameters : args`);
return new Class(...args);
}
Benefits to use Decorators
- Decorators allow you to add new functionality to an object without modifying its code. This can be useful for adding behavior such as logging, error handling, or security checks, without changing the core functionality of the object.
- Decorators can make your code more modular and easier to maintain, as you can add or remove behavior independently of the core functionality of the object.
- Decorators can be easily composed, meaning that you can apply multiple decorators to the same object to achieve a desired behavior.
Drawbacks of using Decorators
- Decorators can make your code more complex, as they add an additional layer of abstraction to your code.
- Decorators can make your code harder to understand, especially if you are using multiple decorators on the same object.
- Decorators are a relatively new feature of JavaScript, and they are not supported in all environments.
- Overall, decorators are a useful tool that can help you add new behavior to your code in a flexible and modular way. However, it's important to consider the potential complexity and readability trade-offs when using decorators in your code.