Les getters et setters sont des fonctions ou des méthodes utilisées pour obtenir et définir les valeurs des variables. Le concept de getter-setter est commun dans la programmation informatique : presque tous les langages de programmation de haut niveau viennent avec un ensemble de syntaxe pour implémenter les getters et setters, y compris JavaScipt.
Lire aussi : 4 Statements JavaScript utiles que vous devez connaître
Dans ce post, nous verrons ce que sont les getters setters, et comment les créer et les utiliser en JavaScript.
- Getters-setters et encapsulation
- Créer des getters et setters
- 1. Avec des méthodes
- 2. Avec des mots-clés
- Quel est le meilleur moyen ?
- Prévention de la surécriture
- Opérations à l’intérieur des getters et setters
- Protéger les données avec les getters et setters
- Données non protégées
- 1. Block scoping
- 2. Scoping des fonctions
- 3. Protection des données sans scoping
- Quand devriez-vous utiliser des getters et des setters ?
Getters-setters et encapsulation
L’idée de getters et setters est toujours mentionnée en conjonction avec l’encapsulation. L’encapsulation peut être comprise de deux façons.
Premièrement, c’est la mise en place du trio données-getters-setters pour accéder et modifier ces données. Cette définition est utile lorsque certaines opérations, telles que la validation, doivent être effectuées sur les données avant de les sauvegarder ou de les visualiser – les getters et setters fournissent le foyer parfait pour cela.
Deuxièmement, il existe une définition plus stricte selon laquelle l’encapsulation est faite pour cacher les données, pour les rendre inaccessibles depuis un autre code, sauf à travers les getters et setters. De cette façon, nous ne finissons pas par écraser accidentellement des données importantes avec un autre code du programme.
Créer des getters et setters
1. Avec des méthodes
Puisque les getters et setters sont fondamentalement des fonctions qui récupèrent/modifient une valeur, il y a plus d’une façon de les créer et de les utiliser. La première façon est :
var obj = { foo: 'this is the value of foo', getFoo: function() { return this.foo; }, setFoo: function(val) { this.foo = val; }}console.log(obj.getFoo());// "this is the value of foo"obj.setFoo('hello');console.log(obj.getFoo());// "hello"
C’est la façon la plus simple de créer des getters et setters. Il y a une propriété foo
et il y a deux méthodes : getFoo
et setFoo
pour retourner et affecter une valeur à cette propriété.
2. Avec des mots-clés
Une façon plus « officielle » et plus robuste de créer des getters et des setters est d’utiliser les mots-clés get
et set
.
Pour créer un getter, placez le mot-clé get
devant une déclaration de fonction qui servira de méthode de getter, et utilisez le mot-clé set
de la même façon pour créer un setter. La syntaxe est la suivante :
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}console.log(obj.foo);// "this is the value of foo"obj.foo = 'hello';console.log(obj.foo);// "hello"
Notez que les données ne peuvent être stockées que sous un nom de propriété (fooVal
) différent du nom des méthodes getter-setter (foo
) car une propriété contenant le getter-setter ne peut pas contenir les données également.
Quel est le meilleur moyen ?
Si vous choisissez de créer des getters et setters avec des mots-clés, vous pouvez utiliser l’opérateur d’affectation pour définir les données et l’opérateur point pour obtenir les données, de la même manière que vous accédez/réglerez la valeur d’une propriété ordinaire.
Cependant, si vous choisissez la première façon de coder les getters et setters, vous devez appeler les méthodes setter et getter en utilisant la syntaxe d’appel de fonction car ce sont des fonctions typiques (rien de spécial comme celles créées à l’aide des mots-clés get
et set
).
De plus, il y a une chance que vous finissiez par assigner accidentellement une autre valeur aux propriétés qui contenaient ces méthodes getter-setter et que vous les perdiez complètement ! Quelque chose dont vous n’avez pas à vous soucier dans la dernière méthode.
Donc, vous pouvez voir pourquoi j’ai dit que la deuxième technique est plus robuste.
Prévention de la surécriture
Si pour une raison quelconque vous préférez la première technique, faites en sorte que les propriétés détenant les méthodes getter-setter soient en lecture seule en les créant en utilisant Object.defineProperties
. Les propriétés créées via Object.defineProperties
, Object.defineProperty
et Reflect.defineProperty
se configurent automatiquement en writable: false
, ce qui signifie lecture seule:
/* Overwrite prevention */var obj = { foo: 'this is the value of foo'};Object.defineProperties(obj, { 'getFoo': { value: function () { return this.foo; } }, 'setFoo': { value: function (val) { this.foo = val; } }});obj.getFoo = 66;// getFoo is not going to be overwritten!console.log(obj.getFoo());// "this is the value of foo"
Opérations à l’intérieur des getters et setters
Une fois que vous avez introduit les getters et setters, vous pouvez aller de l’avant et effectuer des opérations sur les données avant de les modifier ou de les retourner.
Dans le code ci-dessous, dans la fonction getter, les données sont concaténées avec une chaîne de caractères avant d’être retournées, et dans la fonction setter, une validation pour savoir si la valeur est un nombre ou non est effectuée avant de mettre à jour n
.
var obj = { n: 67, get id() { return 'The ID is: ' + this.n; }, set id(val) { if (typeof val === 'number') this.n = val; }}console.log(obj.id);// "The ID is: 67"obj.id = 893;console.log(obj.id);// "The ID is: 893"obj.id = 'hello';console.log(obj.id);// "The ID is: 893"
Protéger les données avec les getters et setters
Jusqu’ici, nous avons couvert l’utilisation des getters et setters dans le premier contexte de l’encapsulation. Passons au second, c’est-à-dire comment cacher les données du code extérieur à l’aide des getters et setters.
Données non protégées
La mise en place de getters et setters ne signifie pas que les données ne peuvent être accédées et modifiées que par ces méthodes. Dans l’exemple suivant, on la modifie directement sans toucher aux méthodes getter et setter :
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}obj.fooVal = 'hello';console.log(obj.foo);// "hello"
On n’a pas utilisé le setter mais on a directement modifié la donnée (fooVal
). Les données que nous avons initialement définies à l’intérieur de obj
ont disparu maintenant ! Pour éviter que cela ne se produise (accidentellement), vous avez besoin d’une certaine protection pour vos données. Vous pouvez le faire en limitant la portée de l’endroit où vos données sont disponibles. Vous pouvez le faire soit par le block scoping ou le function scoping.
1. Block scoping
Une façon est d’utiliser un block scope à l’intérieur duquel les données seront définies en utilisant le mot-clé let
qui limite sa portée à ce bloc.
Un block scope peut être créé en plaçant votre code à l’intérieur d’une paire d’accolades. Chaque fois que vous créez une portée de bloc, assurez-vous de laisser un commentaire au-dessus demandant que les accolades soient laissées seules, afin que personne ne supprime les accolades par erreur en pensant qu’il s’agit d’accolades supplémentaires redondantes dans le code ou ajoutez un label à la portée du bloc.
/* BLOCK SCOPE, leave the braces alone! */{let fooVal = 'this is the value of foo';var obj = { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect the fooVal inside the blockconsole.log(obj.foo);// "this is the value of foo"
Changer/créer fooVal
en dehors du bloc n’affectera pas les fooVal
référencés à l’intérieur des getters setters.
2. Scoping des fonctions
La façon la plus courante de protéger les données avec le scoping est de garder les données à l’intérieur d’une fonction et de retourner un objet avec les getters et setters de cette fonction.
function myobj(){ var fooVal = 'this is the value of foo'; return { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect our original fooValvar obj = myobj();console.log(obj.foo);// "this is the value of foo"
L’objet (avec le getter-setter foo()
à l’intérieur) renvoyé par la fonction myobj()
est enregistré dans obj
, puis obj
est utilisé pour appeler le getter et le setter.
3. Protection des données sans scoping
Il existe également une autre façon de protéger vos données contre l’écrasement sans limiter leur portée. La logique derrière cela est la suivante : comment pouvez-vous modifier une donnée si vous ne savez pas comment elle est appelée ?
Si la donnée a un nom de variable/propriété pas si facilement reproductible, il y a de fortes chances que personne (même nous) ne finisse par l’écraser en assignant une certaine valeur à ce nom de variable/propriété.
var obj = { s89274934764: 'this is the value of foo', get foo() { return this.s89274934764; }, set foo(val) { this.s89274934764 = val; }}console.log(obj.foo);// "this is the value of foo"
Voyez, c’est une façon de faire les choses. Bien que le nom que j’ai choisi ne soit pas vraiment bon, vous pouvez aussi utiliser des valeurs ou des symboles aléatoires pour créer des noms de propriétés comme c’est proposé par Derick Bailey dans cet article de blog. L’objectif principal est de garder les données cachées des autres codes et de laisser une paire getter-setter y accéder/mettre à jour.
Quand devriez-vous utiliser des getters et des setters ?
Vient maintenant la grande question : devez-vous commencer à attribuer des getters et des setters à toutes vos données maintenant ?
Si vous cachez des données, alors il n’y a pas d’autre choix.
Mais si vos données sont vues par d’autres codes, avez-vous encore besoin d’utiliser des getters setters juste pour les regrouper avec du code qui effectue certaines opérations sur elles ? Je dirais que oui. Le code s’additionne très vite. Créer des micro-unités de données individuelles avec son propre getter-setter vous fournit une certaine indépendance pour travailler sur lesdites données sans affecter les autres parties du code.
Lire aussi : 10 raisons pour lesquelles vous avez besoin d’une optimisation du code
.