Decorators are the way of wrapping one piece of code with another or apply a wrapper around a function in JavaScript. Decorators are the design pattern that allows behavior to be added to an individual object, either statically or dynamically without affecting the behavior of other objects from the same class. They are used to enhance the functionality of the function without modifying the underlying function. They are just modifying the behavior of the function or method passed to it by returning a new function. Decorators have already been used in languages like Python and C#, now it is used in JavaScript also.
- Syntax:
let variable=function(object) { object.property='characteristic'; } // Use as decorator @variable class GFG { } console.log(GFG.property);
- Example: This example implements the work of decorators using lower version of JavaScript. In this example, the function add takes the print function as parameter. Here print function works as decorator. It helps to concatenate the “is best” string to the passed string “GFG”. Here we use the “add” function for extending and executing the “print” function by concatenate the string.
<script>
// 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) {
document.write(s);
}
// Calling "add"
var
g = add(print);
g(
'GFG'
);
</script>
- Output:
GFG is Best
Procedure to run: To run decorators, it requires transpiler support in browsers but at present no browsers are supported this.
- Step1: We use BabelJS and then we can run decorators on browsers. Here we will use jsfiddle for run the code.
- Step 2: Under jsfiddle select the option Babel+JSX, otherwise the program will not run, only JavaScript will show error @.
Below example illustrates the decorators in JavaScript:
- Example 1:
let variable =
function
(target) {
target.proprty =
'GFG is best'
;
}
// Decorator
@variable
class GFG
{ }
// Print in the console
console.log(GFG.proprty);
- Output:
GFG is best
- Example 2:
let variable =
function
(color) {
return
function
(target) {
target.proprty = color;
}
};
// The value is passed in the decorator
@variable(
'GFG is Green'
)
class GFG
{ }
console.log(GFG.proprty);
- Output:
GFG is Green
Why Use Decorators ?
We can decorate a code using decorators but this decorating is difficult to apply and also apply the same techniques to other pieces of code or wrapping one piece of code with another or apply a wrapper around a function is difficult. In ES6, decorators can resolve these difficulties. Decorators allow an efficient and understandable way of wrapping a piece of code with another function or another code. Also, decorators provide a clear syntax for applying these wrappers. JavaScript decorators are not directly supported but in the future may be decorators support will be added to javascript.
Types of Decorator: 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 decorator are supported now:
- Class member decorators
- Members of classes
Class member decorators: These decorators are applied to a single member of a class. This decorator has properties, methods, getters, setters. This decorator accepts 3 parameters:
- target: The class in 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: In this example, we are passing two arguments and here the decorator function checks if the descriptor is function or not and then it prints the arguments and the addition of them. The function add is decorated here using another function gfg.
// Decorator function
function
gfg(target, name, descriptor) {
var
fn = descriptor.value;
// Checks if "descriptor.value"
// is a function or not
if
(
typeof
fn ==
'function'
) {
descriptor.value =
function
(...args) {
// Document.write(`parameters: ${args}`+"<br>");
console.log(`parameters: ${args}`);
var
result = fn.apply(
this
, args);
// Document.write(`addtion: ${result}`);
// Print the addition of passed arguments
console.log(`addition: ${result}`);
return
result;
}
}
return
descriptor;
}
class geek {
@gfg
add(a, b) {
return
a + b;
}
}
var
e =
new
geek();
e.add(100, 200);
- Output:
parameters: 100, 200 addition: 300
- Example 2:
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
Members of classes: 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:
function
log()
// Decorator function
{
return
function
decorator()
{
// "arrow" function
return
(...args) =>
{
console.log(`Parameters : args`);
return
new
Class(...args);
};
}
}
// Decorators
@log
class gfg
{
constructor(name, category) {}
}
const e =
new
gfg(
'geek'
,
'code'
);
// Arguments for Demo: args
console.log(e);
- Output:
(...args) => { console.log(`Parameters : args`); return new Class(...args); }