Prologue
Je veux présenter à votre cour un certain nombre de mini-statues qui décriront les techniques et les principes fondamentaux de la métaprogrammation. J'écrirai principalement sur l'utilisation de certaines techniques en JavaScript ou en TypeScript
Ceci est le premier (et, espérons-le, pas le dernier) article de la série.
Alors qu'est-ce que la métaprogrammation
La métaprogrammation est une technique de programmation dans laquelle les programmes informatiques ont la capacité de traiter d'autres programmes comme leurs données. Cela signifie qu'un programme peut être conçu pour lire, générer, analyser ou transformer d'autres programmes, et même se modifier en cours d'exécution. Dans certains cas, cela permet aux programmeurs de minimiser le nombre de lignes de code pour exprimer une solution, réduisant ainsi le temps de développement .
Une description assez confuse, mais le principal avantage de la métaprogrammation est compréhensible:
... cela permet aux programmeurs de minimiser le nombre de lignes de code pour implémenter la solution, ce qui à son tour réduit le temps de développement

En fait, la métaprogrammation a beaucoup de visage et de prétention. Et vous pouvez discuter pendant longtemps sur «où la métaprogrammation se termine et la programmation elle-même commence»
Pour ma part, j'ai accepté les règles suivantes:
- La métaprogrammation ne traite pas de la logique métier, ne la modifie pas et ne l'affecte en aucune façon.
- Si vous supprimez tout le code de métaprogrammation, cela ne devrait pas (radicalement) affecter le programme.
En JavaScript, la métaprogrammation est une tendance relativement nouvelle, dont la brique de base est le descripteur.
Descripteur JavaScript
Le descripteur est une sorte de description (méta-informations) d'une certaine propriété ou méthode dans un objet.
Comprendre et manipuler correctement cet objet ( descripteur ) permet bien plus que de simplement créer et modifier des méthodes ou des propriétés dans des objets.
Le descripteur aidera également à comprendre le travail avec les décorateurs (mais plus à ce sujet dans le prochain article).
Pour plus de clarté, imaginez que notre objet soit une description de l'appartement.
Nous décrivons l'objet de notre appartement:
let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} };
Déterminons lesquelles des propriétés sont modifiables et lesquelles ne le sont pas.
Par exemple, le sol ou la taille totale de l'appartement ne peuvent pas être modifiés, mais le nombre de chambres ou salles de bain est tout à fait possible.
Et donc nous avons l'exigence suivante: dans les objets apt , il est impossible de changer les propriétés: sol et taille .
Pour résoudre ce problème, nous avons juste besoin de descripteurs de chacune de ces propriétés. Pour obtenir le descripteur , nous utilisons la méthode statique getOwnPropertyDescriptor , qui appartient à la classe Object .
let descriptor = Object.getOwnPropertyDescriptor(todoObject, 'floor'); console.log(descriptor);
Analysons dans l'ordre:
valeur: quelconque - en fait la même valeur qui à un moment donné a été affectée à la propriété de l' étage
accessible en écriture: booléen - détermine s'il faut ou non modifier la valeur
enumerable: boolean - détermine si la propriété floor peut ou non être listée - (plus à ce sujet plus tard).
configurable: boolean - Définit la possibilité d'apporter des modifications à l'objet descripteur .
Afin d'éviter la possibilité de changer la propriété du sol , après l'initialisation, il est nécessaire de changer la valeur de writable en false .
Pour modifier les propriétés d'un descripteur, il existe une méthode statique defineProperty , qui prend l'objet lui-même, le nom de la propriété et le descripteur .
Object.defineProperty(apt, 'floor', {writable: false});
Dans cet exemple, nous ne transmettons pas l'intégralité de l'objet descripteur , mais uniquement une propriété accessible en écriture avec la valeur false .
Essayons maintenant de changer la valeur de la propriété floor:
apt.floor = 44; console.log(apt.floor);
La valeur n'a pas changé, et lors de l'utilisation de 'use strict' nous obtenons un message d'erreur:
Impossible d'affecter à la propriété en lecture seule 'étage' de l'objet ...
Et maintenant, nous ne pouvons plus changer la valeur. Cependant, nous pouvons toujours retourner en écriture -> true , puis modifier la propriété de l' étage . Pour éviter cela, il est nécessaire de remplacer la valeur de la propriété configurable par false dans le descripteur .
Object.defineProperty(apt, 'floor', {writable: false, configurable: false});
Si nous essayons maintenant de changer la valeur de l'une des propriétés de notre descripteur ...
Object.defineProperty(apt, 'floor', {writable: true, configurable: true});
En réponse, nous obtenons:
TypeError: impossible de redéfinir la propriété: étage
En d'autres termes, nous ne pouvons plus changer la valeur de floor ni son descripteur .
Résumer
Pour que la valeur de la propriété dans l'objet reste inchangée, il est nécessaire d'enregistrer la configuration de cette propriété: {inscriptible: faux, configurable: faux} .
Cela peut être fait comme lors de l'initialisation de la propriété:
Object.defineProperty(apt, 'floor', {value: 12, writable: false, configurable: false});
Ou après.
Object.defineProperty(apt, 'floor', {writable: false, configurable: false});
À la fin, considérons un exemple avec une classe:
class Apartment { constructor(apt) { this.apt = apt; } getFloor() { return this.apt.floor } } let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} };
Modifiez la méthode getFloor:
Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt);
Modifiez maintenant le descripteur de la méthode getFloor () :
Object.defineProperty(Apartment.prototype, 'getFloor', {writable: false, configurable: false}); Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt);
J'espère que cet article jette un peu plus de lumière sur ce qu'est un descripteur et comment il peut être utilisé.
Tout ce qui est écrit ci-dessus ne prétend pas être absolument vrai ou le seul correct.