Fermer

décembre 16, 2019

Décorateurs en JavaScript


Dans cet article, nous aborderons les décorateurs en JavaScript avec leurs cas d'utilisation. Nous découvrirons également tous les concepts liés aux décorateurs avec des exemples pratiques.

Qu'est-ce qu'un décorateur?

Les décorateurs ne sont qu'une enveloppe autour d'une fonction . Ils sont utilisés pour améliorer la fonctionnalité de la fonction sans modifier la fonction sous-jacente . Les décorateurs ne sont pas un nouveau concept – ils ont déjà été utilisés par les développeurs de Python et C #, et même les développeurs JavaScript les ont implémentés depuis toujours. Quelle? Oui, vous l'avez bien entendu.

Les développeurs JavaScript connaissent les fonctions d'ordre supérieur, qui se comportent exactement comme les décorateurs. Pour comprendre les fonctions d'ordre supérieur en JavaScript, nous allons avoir un aperçu détaillé des fonctions en JavaScript.

Fonctions en JavaScript

Les fonctions en JavaScript sont des objets de première classe, c'est-à-dire qu'elles se comportent exactement comme des objets . Nous pouvons les affecter à des variables, les transmettre en tant que paramètres à d'autres fonctions. Ils peuvent également être renvoyés par une autre fonction. Prenons quelques exemples pour comprendre tous ces concepts.

Fonctions assignées à une variable


 var   helloWorldFunc   =   fonction  ()   {
console .  log  ( "Hello, world" ) 
} 



 var  anotherVar  =  helloWorldFunc


 helloWorldFunc  ()  
 anotherVar  ()     

Ici, helloWorldFunc pointe vers une définition de fonction qui imprime Bonjour, monde . Lorsque helloWorldFunc est affecté à anotherVar les deux variables commencent à pointer vers la même définition de fonction. Par conséquent, lorsque les deux fonctions sont appelées, elles impriment la même sortie – Hello, world .

Fonctions passées en paramètre à une autre fonction


 var   printHello   = [19659011] fonction  ()   {
console .  log  ( "Je suis la fonction printHello" ) 
} 



 fonction   printHelloAndHi  ( func )   {
	 func  () 
console .  log  ( "Je suis la fonction printHelloAndHi" ) 
} 


 printHelloAndHi  ( printHello ) 

Ici , nous avons passé la fonction printHello comme paramètre à la fonction printHelloAndHi donc la fonction printHelllo est affectée à la variable func . printHelloAndHi la fonction appelle func (c.-à-d. printHello ) dans sa définition.

Fonction renvoyée par une autre fonction

 fonction   printAdditionFunc  ( x  y )   {
	 var   addNumbers   =   fonction   ()   {
résultat  =  x  +  y ; 
console .  log  ( "Ajout de"   +  x  +   "et"   +  y  +   "est:"   +  résultat ) ; 
	} 
    
	
	 return  addNumbers
} 


 var  addNumbersFunc  =   printAdditionFunc  ( 3   4 ) 

console .  log  ( addNumbersFunc ) 

 addNumbersFunc  ()  

 Sortie: fonction renvoyée par une autre fonction

Ici, [19459091] Ici, ] La fonction printAdditionFunc prend deux paramètres x et y . Il renvoie définition de la fonction addNumbers qui est affectée à addNumbersFunc . Lorsque addNumbersFunc est appelé, il renvoie l'ajout de 3 et 4 . Comment 3 et 4 qui ont été passés à printAdditionFunc accessibles à l'intérieur de addNumbersFunc ? Cela est dû à fermetures dans JavaScript . Les fermetures permettent aux fonctions enfant d'accéder aux variables de fonction parent, aux objets, etc. En d'autres termes, les fonctions enfant stockent les variables de fonction parent afin que les fonctions enfant puissent les utiliser à tout moment.

Dans la fonction ci-dessus, nous pouvons vérifier que addNumbersFunc stocke les valeurs de 3 et 4 . La façon dont JavaScript stocke ces variables peut être comprise à partir de cet article .

Maintenant que les bases des fonctions sont claires, comprenons les fonctions d'ordre supérieur.

Fonctions d'ordre supérieur en JavaScript

Les fonctions d'ordre sont des fonctions en JavaScript qui prennent une autre fonction en paramètre, ajoutent quelques opérations par-dessus et renvoient la fonction. Les fonctions printAdditionFunc et printAddition mentionnées dans les exemples ci-dessus sont toutes deux des fonctions d'ordre supérieur. Prenons quelques exemples de fonctions d'ordre supérieur.

 fonction   printMessage  ( message )   {
	 return   fonction  ()   {
console .  log  ( message ) 
	} 
} 


 var  printHello  =   printMessage  ([[19659017] "Bonjour" )  
 printHello  ()  


 var  printHi  =   printMessage  ( "Salut"  )  
 printHi  ()  

Ici, printMessage est une fonction d'ordre supérieur. C'est comme une fonction d'usine qui renvoie une nouvelle fonction.

Un autre exemple plus utile



 fonction   handleException  ( funcAsParameter )   {
console .  log  ( "Inside handleException function" ) 
	 try   {
		 funcAsParameter  () 
	} [19659011] capture  ( err )   {
console .  log  ( err ) 
	} 
} 

 fonction   divideByZero  ()   {
result  =   5   /   0 
	 if  (!  Numéro .  isFinite  ( result ) )   {
		 lancer   "La division par zéro n'est pas une bonne idée !!" 
	} 
console .  log  ( "Le résultat de la division de 5 par zéro est:"   +  résultat ) 
} 



 handleException  ( divideByZero ) 

 Sortie: exemple d'exception de poignée

Ici, la fonction handleException est une fonction d'ordre supérieur. Il peut être utilisé par n'importe quelle fonction pour gérer l'exception. Avec la fonction handleException nous n'avons pas à gérer les exceptions pour chaque fonction séparément, nous pouvons simplement passer chaque fonction à la fonction handleException et ils obtiendront la fonctionnalité pour gérer les exceptions de la handleException . Lorsque nous transmettons divideByZero en tant que paramètre à la fonction handleException l'exception déclenchée par divideByZero est gérée par le code de fonction handleException . [19659177] Si vous examinez attentivement la définition des fonctions et des décorateurs d'ordre supérieur, vous constaterez qu'ils sont similaires. Les fonctions d'ordre supérieur se comportent exactement comme les décorateurs. Tout comme les fonctions d'ordre supérieur, les décorateurs ajoutent également des fonctionnalités à la fonction existante sans modifier le code sous-jacent. Dans notre cas, handleException est une fonction de décorateur.

Alors, pourquoi les décorateurs?

Si nous avons déjà un décorateur comme fonction d'ordre supérieur, alors pourquoi avons-nous besoin de décorateurs? Les fonctions d'ordre supérieur peuvent servir de décorateurs à la fonction JavaScript, mais l'invention des classes en JavaScript nous a présenté les méthodes de classe – c'est-à-dire les fonctions définies à l'intérieur de la classe où les fonctions d'ordre supérieur n'ont pas agi comme décorateurs . Pour comprendre pourquoi une méthode de classe ne peut pas être utilisée avec des fonctions d'ordre supérieur, commençons par comprendre: ce que sont les classes en JavaScript.

Classes en JavaScript

Avant le mot-clé de classe

Avant le Le mot-clé class a été introduit par ES2015, si nous voulions créer des classes, c'est-à-dire un plan pour les objets, en JavaScript, nous avons pris l'aide de prototypes et de la fonction constructeur .

Considérons un exemple de la constructeur fonction:


 fonction   Humain  ( firstName  lastName )   {
	 this . .  ] firstName  =  firstName
 this .  lastName  =  lastName
} 


Humain .  prototype .  getFullName   =   function  ()   {
	 return   this . .  firstName  +   ""   +   this .  lastName
} 

 var  person1  =   new   Human  ( "Virat"   "Kohli" ) [19659212] var  person2  =   new   Human  ( "Rohit"   "Sharma" ) 

console .  log  ( person1 ) 

La fonction Human est une fonction constructeur avec des propriétés firstName et nom . Avec l'aide de cette fonction humaine nous pouvons créer de nouveaux objets. Ces objets auront comme propriétés firstName lastName et getFullName .

Chaque fonction constructeur a une propriété prototype qui est un objet. Les propriétés ajoutées au prototype seront partagées entre tous les objets créés à partir de la fonction constructeur. Les propriétés du prototype peuvent être vérifiées comme suit:

 Humain .  prototype

 Les propriétés des prototypes de l'objet humain La méthode

getFullName est définie sur la propriété prototype de la fonction Humain . Chaque objet créé à l'aide de la fonction constructeur Humain aura sa propre copie de firstName et lastName c'est-à-dire person1 aura firstName valeur comme Virat tandis que personne2 aura firstName valeur comme Rohit .

Le getFullName La fonction sera partagée entre tous les objets créés à l'aide de la fonction constructeur, c'est-à-dire que person1 et person2 auront la même copie de getFullname . Si person1 change getFullName person2 aura également cette modification getFullName . En effet, les propriétés de prototype de la fonction constructeur sont partagées entre tous les objets.

 Prototypes en JavaScript

Nous aurions également pu définir getFullName à l'intérieur de Human fonction constructeur et il aurait été parfaitement bien. Mais alors, person1 et person2 auraient des copies différentes de la fonction getFullname qui seraient redondantes. Inutilement, cela aurait consommé de la mémoire supplémentaire. Par conséquent, la fonction getFullName est définie sur le prototype humain afin que tous les objets créés à l'aide de la fonction constructeur humain aient la même copie de getFullName

Ce ne sont que les bases des prototypes pour les décorateurs. Si vous voulez comprendre ce concept important et merveilleux de prototypes, vous pouvez lire cet article

Mot-clé de classe

Les classes en JavaScript, introduites dans ES2015, ne sont pas comme les classes en Java, C # ou Python. C'est juste du sucre syntaxique sur le comportement basé sur un prototype. Le sucre syntaxique signifie que JavaScript vous permet de définir des classes à l'aide du mot-clé class mais, sous le capot, il utilise toujours des prototypes et des fonctions constructeurs comme indiqué ci-dessus pour créer des objets.

Déclaration de classe

 class   Humain   {
	 constructeur  ( firstName  lastName )   {
		 this .  firstName  =  prénom
 this .  lastName  =  lastName
} 
	
	 getFullName  ()   {
		 return   this .  firstName  +   ""   + [19659011] ceci .  lastName
} 
} 

 typeof  ( Human )  

Le code ci-dessus déclare une classe Human avec une fonction constructor function constructeur . Sous le capot, JavaScript convertira cette classe en fonction constructeur Humain . Toutes les fonctions définies à l'intérieur de la classe seront attachées à la propriété prototype de la fonction constructeur.

Le code ci-dessous affiche la conversion des classes en constructeurs et prototypes:

 fonction   Humain  ( firstName  lastName )   {
	 this .  firstName  =  firstName
 this .  lastName  =  lastName
} 

Humain .  prototype .  getFullName   =   function  ()   {
	 return   this . .  firstName  +   ""   +   this .  lastName
} 

Ceci est similaire à la fonction constructeur que nous avons discutée ci-dessus. C'est pourquoi les classes sont appelées sucres syntaxiques en JavaScript.

 Fonction constructeur humain

Dans l'image ci-dessus, nous pouvons voir que le type de Humain est une fonction. De plus, si nous comparons le Human.prototype des deux classe Human et constructeur fonction Human ils sont les mêmes sauf que la propriété constructeur de la classe Human a une propriété constructor avec une valeur class Human tandis que dans le cas de la fonction constructeur, il s'agit de ƒ Human (firstName, lastName) comme indiqué ci-dessous :

 Fonction constructeur et comparaison de classes

Nous pouvons créer des objets en utilisant la classe avec nouveau mot-clé comme indiqué ci-dessous.

 humanObj  =   nouveau   Humain  ( "Virat"   "Kohli" ) 
console .  log  ( humanObj ) 

Voyons maintenant pourquoi les fonctions d'ordre supérieur ne fonctionnent pas avec les méthodes de classe.

Problèmes d'utilisation de fonctions d'ordre supérieur avec la classe Méthodes


 fonction   log  ( functionAsParameter )   {
	 return   fonction  ()   {
console .  log  ( "Execution of"   +  functionAsParameter .  name  +   "begin" ) 
		
		 functionAsParameter  () 
console .  log  ( "Execution of"   +  functionAsParameter .  nom  +   "fin" ) 
	} 	
} 

 classe   Humain   {
	 constructeur  ( firstName  lastName )   {[19659165] ceci .  firstName  =  firstName
 this .  lastName  =  lastName
} 
	
	 getFullName  ()   {
		 return   this .  firstName  +   ""   + [19659011] ceci .  lastName
} 
} 

 var  humanObj  =   nouveau   Humain  ( "Virat"   "Kohli" [19659012]) 


 var  newGetFullNameFunc  =   log  ( humanObj .  getFullName ) 
 newGetFullNameFunc  ([19659012)

 Erreur lors de l'utilisation de la fonction d'ordre supérieur avec les méthodes

Le code ci-dessus déclare une fonction log comme fonction d'ordre supérieur qui prend une autre fonction comme paramètre. La fonction log est utilisée pour la journalisation. Nous avons créé un objet humanObj . Essayons d'utiliser la fonction log avec la fonction getFullName . La fonction newGetFullNameFunc est une fonction modifiée getFullName capable de se connecter. Définition de newGetFullNameFunc :

 var   newGetFullNameFunc   =   fonction  ()   {
console .  log  ( "Execution of"   +  functionAsParameter .  name  +   "begin" ) 
	 functionAsParameter  () 
console .  log  ( "Execution of"   +  functionAsParameter .  nom  +   "fin" ) 
}  

Lorsque newGetFullNameFunc est appelé, il appelle en interne functionAsParameter c'est-à-dire la fonction getFullName . Lorsque la fonction getFullName est appelée depuis newGetFullNameFunc la valeur de this à l'intérieur de getFullName est indéfinie . D'où le code casse. Pour corriger cela, nous pouvons modifier notre fonction log comme indiqué ci-dessous:

 fonction   log  ( classObj  functionAsParameter ) [19659014] {
	 return   fonction  ()   {
console .  log  ( "Execution of"   +  functionAsParameter .  name  +   "begin" ) 
functionAsParameter .  appeler  ( classObj ) 
console .  log  ( "Execution of"   +  functionAsParameter .  nom  +   "fin" ) 
	} 	
} 

 classe   Humain2   {
	 constructeur  ( firstName  lastName )   {[19659165] ceci .  firstName  =  firstName
 this .  lastName  =  lastName
} 
	
	 getFullName  ()   {
		 return   this .  firstName  +   ""   + [19659011] ceci .  lastName
} 
} 

 var  humanObj  =   nouveau   Human2  ( "Virat"   "Kohli" [19659012]) 
 var  newGetFullNameFunc  =   log  ( humanObj  humanObj .  getFullName ) 
 newunc  () 

Dans le code ci-dessus, nous passons également l'objet humanObj en tant que paramètre à la fonction log . Ceci est fait pour préserver la valeur de this . Dans newGetFullNameFunc nous appelons functionAsParameter (c'est-à-dire getFullName ) à l'aide de la fonction call . La fonction call appellera getFullName sur l'objet humanObj c'est-à-dire maintenant la valeur de cet à l'intérieur de getFullName sera humanObj qui a firstName et lastName comme propriétés. Par conséquent, ce code fonctionne parfaitement.

Pour rendre la syntaxe du décorateur plus familière aux développeurs, une nouvelle syntaxe décoratrice a été proposée qui est similaire à la syntaxe du décorateur dans d'autres langues. Nous allons examiner cette syntaxe, mais d'abord, discutons des descripteurs de propriété qui nous aideront à comprendre les décorateurs.

Descripteurs de propriété

Chaque propriété d'objet en JavaScript a des descripteurs de propriété qui sont utilisés pour décrire les attributs ou les métadonnées de la propriété. Les descripteurs de propriété sont eux-mêmes un objet. Voici les descripteurs de propriété associés à chaque propriété d'un objet:

  1. valeur : valeur actuelle de la propriété de l'objet.
  2. inscriptible : true ou false . La valeur par défaut est true . Indique si la propriété est accessible en écriture ou non.
  3. énumérable : vrai ou faux . La valeur par défaut est true . Indique si la propriété est énumérable ou non, c'est-à-dire si elle apparaîtra dans l'itération de object.keys .
  4. configurable : true ou false ]. La valeur par défaut est true . Indique si les descripteurs de propriété de la propriété peuvent être modifiés ou non.

Considérez un objet comme indiqué ci-dessous:

 var  humanObj  =   {
	 'firstName' :   'Virat' 
	 'lastName' :   'Kohli' 
	 'getFullName' :   fonction  ( ])   {
		 retourner   ce .  firstName  +   ""   +   this .  lastName  ; 
	} 
} 

Ici, nous avons un objet humanObj . Chaque propriété ( firstName lastName et getFullName ) aura ses propres descripteurs de propriété. Nous pouvons vérifier la valeur du descripteur de propriété à l'aide de getOwnPropertyDescriptor comme illustré ci-dessous:

 Object .  getOwnPropertyDescriptor  ( humanObj [19659071] 'firstName' ) 

getOwnPropertyDescriptor répertorie tous les descripteurs de propriété de firstName . De même, lastName et getFullName auront leurs propres descripteurs de propriété.

object.defineProperty

object.defineProperty peut être utilisé pour définir de nouvelles propriétés ou mettre à jour une propriété existante d'un objet. object.defineProperty prend les paramètres suivants:

  1. objet : objet sur lequel une nouvelle propriété doit être créée ou une propriété existante doit être mise à jour.
  2. nom : nom du propriété.
  3. descripteur : objet descripteur de propriété.

Définissons une nouvelle propriété avec âge sur humanObj .

 objet . . defineProperty  ( humanObj   'âge'   { valeur :   10 } )  ; 
Object .  getOwnPropertyDescriptor  ( humanObj   'age' ) 

Pour la propriété age comme nous l'avons fourni uniquement un descripteur de propriété value comme paramètre de defineProperty les autres descripteurs de propriété auront les valeurs par défaut. Si la propriété existe déjà sur l'objet, Object.defineProperty écrasera la propriété avec les nouveaux descripteurs de propriété comme indiqué ci-dessous.

 Object .  defineProperty  ( humanObj   'firstName'   { valeur :   "Rohit" } ) ; 
console .  log  ( humanObj ) 

Pour empêcher l'utilisateur de modifier la valeur d'une propriété d'objet, nous pouvons la rendre en lecture seule en changeant inscriptible en faux .

 Objet .  defineProperty  ( humanObj   'firstName' [19659012]  { inscriptible :   false } ) ; 

Maintenant, si nous essayons de changer la propriété, cela ne changera pas. [19659224] humanObj . firstName = 'Virat'
console . log ( humanObj )

Si nous ne voulons pas que les utilisateurs modifient les descripteurs de propriété, nous pouvons les empêcher en définissant la propriété configurable descripteur de false .

Maintenant, avec la compréhension de tous les concepts ci-dessus, comprenons enfin les décorateurs.

Remarque : Comme les décorateurs ne font pas actuellement partie de JavaScript, les extraits de code ci-dessous ne peuvent pas être exécutés dans le navigateur. Vous pouvez utiliser JSFiddle pour exécuter et comprendre les extraits de code ci-dessous. Dans JSFiddle, vous devez sélectionner la langue comme Babel + JSX comme indiqué ci-dessous:

 JSFiddle "src =" https://d585tldpucybw.cloudfront.net/sfimages/default-source/ blogs / 2019 / 2019-12 / JSFiddle.png "/> </p>
<h2 id= Décorateur de méthode de classe

Les décorateurs de méthode de classe sont utilisés pour modifier les méthodes de classe en ajoutant des fonctionnalités supplémentaires au-dessus de la méthode. Il se comporte comme des fonctions d'ordre supérieur. . Les décorateurs sont ajoutés au-dessus de la définition de méthode en utilisant @ suivi du nom de la fonction décorateur. Voici la syntaxe du décorateur:


 fonction   decoratorFunc  ( cible  propriété  descripteur )   {} 

 classe   DecoEx   {
    
    @decoratorFunc
     getFullName  ()   {} 
} 

Une fonction décoratrice accepte trois paramètres comme indiqué ci-dessus:

  1. cible : Classe de la méthode sur
  2. propriété : nom de la méthode sur laquelle le décorateur est défini.
  3. descriptor : descripteurs de propriété de la méthode sur laquelle le décorateur a été défini.

le décorateur renvoie l'objet descripteur de propriété. Nous pouvons utiliser la propriété de la valeur du descripteur de propriété pour remplacer la définition de la fonction sous-jacente. Lorsque le moteur JavaScript rencontre le décorateur, il appelle la fonction décorateur en passant la fonction sous-jacente comme paramètre. À l'intérieur de la fonction décorateur, nous définissons une nouvelle fonction au-dessus de la fonction sous-jacente ainsi que de nouvelles lignes de code.

Voyons cela avec un exemple. Considérez le code ci-dessous:


 fonction   readonlyDecorator  ( cible  propriété  descripteur )   {
    console .  journal  ( "Target:"  ) 
    console .  journal  ( cible ) 
    
    console .  journal  ( " nNom du bien" ) 
    console .  journal  ( propriété ) 
    
    console .  log  ( " nDescriptor property" ) 
    console .  journal  ( descripteur ) 
    
    
    descripteur .  accessible en écriture  =   faux 
    
     retour  descripteur
} 

 classe   Humain   {
	 constructeur  ( firstName  lastName )   {
		 this  ].  firstName  =  firstName
 this .  lastName  =  lastName
} 


@readonlyDecorator
 getFullName  ()   {
		 retourner   ceci .  firstName  +   ""   +   ceci [19659012].  nom
} 
} 

humanObj  =   nouveau   Humain  ( "Virat"   "Kohli" ) 

console .  log  ( " ngetFullName property value" ) 
console .  log  ( humanObj .  getFullName ) 


humanObj .  getFullName  =   "Bonjour" 


console .  log  ( " nAprès avoir modifié la valeur getFullName" ) 
console .  log  ( humanObj .  getFullName ) 

Trouvez le code JSFiddle ici .

 exemple de décorateur en lecture seule

Nous voulons que la méthode getFullName soit en lecture seule. Hence, we have defined the @readonlyDecorator decorator on top of the getFullName method. @readonlyDecorator takes Human.prototypegetFullName method name i.e. getFullName and property descriptor of getFullName as parameters.

Here, the JavaScript engine will first call the readonlyDecorator and then getFullName function. When readonlyDecorator is called it modifies the getFullName function using the property descriptor’s value property, so when JavaScript calls the getFullName function, it is calling the modified getFullName function. Let’s check the steps taken by the JavaScript engine to execute the decorators.

funcDescriptor = Object.getOwnPropertyDescriptor(humanObj, 'getFullName')


descriptor = readonlyDecorator(Human.prototype, 'getFullName', funcDescriptor)



Object.defineProperty(Human.prototype, 'getFullName', descriptor);

You can comment the @readonlyDecorator code from the top of getFullName method and then try to modify the getFullName method.

Now, we will see how to define decorators with parameters. Consider the example below:

function log(message) {
  function actualLogDecorator(target, property, descriptor) {
    console.log("ngetFullName descriptor's value property")
    console.log(descriptor.value)

    var actualFunction = descriptor.value;

    var decoratorFunc = function() {
      console.log(message)
      
      return actualFunction.call(this)
    }

    
    
    descriptor.value = decoratorFunc

		console.log("nNew descriptor's value property due to decorator")
    console.log(descriptor.value)

    
    return descriptor
  }

  return actualLogDecorator
}

class Human {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  @log("Logging by decorator")
  getFullName() {
    return this.firstName + " " + this.lastName
  }
}

humanObj = new Human("Virat", "Kohli")

console.log("ngetFullName's descriptor's value property after modification due to decorator")


descriptorObject = Object.getOwnPropertyDescriptor(Human.prototype, 'getFullName')
console.log(descriptorObject.value)

console.log("nOutput for getFullName method")
console.log(humanObj.getFullName())

Find JSFiddle code here.

Example of decorator with parameters

Steps followed by JavaScript:

var funcDescriptor = Object.getOwnPropertyDescriptor(humanObj, 'getFullName')


var actualDecorator = log("This is done using decorators") 

descriptor = actualDecorator(Human.prototype, 'getFullName', funcDescriptor)
Object.defineProperty(Human.prototype, 'getFullName', descriptor);

Here, JavaScript first calls the log method with the parameter. log method returns the actualLogDecorator function. actualLogDecorator is a modified getFullName method with logging functionality added on top of the getFullName function.

JavaScript treats the actualLogDecorator as the decorator function, so actualLogDecorator returns the descriptor which has the modified getFullName function definition.

After this, the steps followed by JavaScript to execute the getFullName with the decorator function are same as discussed in the example of a decorator without a parameter.

Class Decorators

Class decorators are defined at the top of the class, unlike method decorators which are declared at the top of the class method. Class decorators need to return the constructor function or a new class, unlike method decorators which need to return the property descriptor. Class decorators accept only one parameter — that is the class on which they are defined. Let’s have a look at the class decorators example.

@decFunc
class Foo {
}



function Foo(FooClass) {
}


NewFoo = decFunc(Foo) || Foo


Foo = NewFoo

The above code explains the steps involved in the class decorator execution. We have an empty class Foo with decorator function decFunc defined on top of the class Foo. decorFunc accepts the class (or the constructor function in ES2015) as a parameter, modifies it and then returns the modified class (or the constructor function in ES2015) which is assigned to NewFoo variable. The NewFoo variable overwrites the Foo so that now Foo has the modified functionality, i.e. NewFoo functionality. Let’s consider another example:

function  newConstructor( HumanClass ) {
	
	var  newConstructorFunc  =  function(firstName, lastName, age) {
		this.firstName  =  firstName
		this.lastName  =  lastName
		this.age  =  age
	}

	return  newConstructorFunc
} 

@newConstructor
class  Human {
	constructor( firstName, lastName ) {
		this.firstName  =  firstName;
		this.lastName  =  lastName;
	}
}



var  person1  =  new  Human("Virat", "Kohli", 31);
console.log( person1 );


console.log( Human.prototype.constructor );
console.log(person1.__proto__.constructor);

Find JSFiddle code here.

Here, the newConstructor decorator is defined on the Human class. The newConstructor decorator returns a constructor function with firstNamelastName and age parameters. The returned constructor function overwrites the existing Human constructor function. Due to this, we are able to pass the ViratKohli and 31 parameters to the Human constructor function.

Instead of the constructor function, we could have also returned the new class itself as shown below. This NewClass definition will overwrite the Human class constructor function definition.

function newConstructor(HumanClass) {
	return class NewClass {
		constructor(firstName, lastName, age) {
			this.firstName = firstName
			this.lastName = lastName
			this.age = age
		}
	}
}

Conclusion

In this article, we learned the following concepts related to decorators:

  1. Implementation of decorators in JavaScript using higher-order functions
  2. Basic of classes in JavaScript
  3. Property descriptors in javaScript
  4. Class method decorators and Class decorators in JavaScript




Source link