Encapsulation en JavaScript

Ce sera mon dernier post dans la série « JavaScript pour les développeurs (C /)AL » aujourd’hui. Si je continuais à bloguer sur des trucs JavaScript presque purs, vous pourriez raisonnablement demander s’il s’agit en fait d’un blog NAV ou d’un blog JavaScript. C’est toujours NAV, et bien que les choses sur lesquelles je suis sur le point d’écrire soient purement un concept JavaScript, je le trouve très pertinent pour tout développeur de complément de contrôle. Alors, prends ma bière, et prends avec moi une autre.

L’une des plaintes que j’entends souvent à propos de JavaScript est qu’en JavaScript, il n’y a pas d’encapsulation. C’est presque complètement vrai, à l’exception du fait que c’est entièrement faux.

Où est le problème en premier lieu, et ensuite quelle est la solution? Plongeons dedans.

Le problème

Imaginez que vous déclarez un constructeur. Imaginez une seconde que nous sommes dans le monde ES5 (que nous devons malheureusement utiliser si nous voulons que nos compléments de contrôle soient entièrement compatibles avec toutes les plates-formes sur lesquelles NAV et Business Central sont pris en charge).

C’est mon code:

Évidemment, cela montre 42 ans, ce qui est mon âge actuel.

Bientôt, ce sera mon anniversaire, donc cela montrerait 43:

Maintenant, alors que j’aimerais absolument pouvoir faire quelque chose comme ça dans la vraie vie:

… ça n’arrivera pas. Dans un monde orienté objet décent, « age » devrait être une propriété encapsulée, et vous ne devriez pas pouvoir appeler vjeko.âge = 25 ans du tout. Les langages orientés objet C # et « normaux » ont un concept d’encapsulation, ils peuvent le gérer en utilisant des champs privés, mais JavaScript n’a pas de concept de privé. Tout ce qui est défini à l’intérieur du constructeur d’objet (ou plus tard sur une instance d’un objet construit) est entièrement accessible à tout le code qui a accès à cette instance. Dans mon exemple ci-dessus, tout le monde peut définir l’âge et s’en tirer.

On pourrait dire que JavaScript n’a pas d’encapsulation. Et comme je l’ai dit plus haut, vous auriez tout à fait tort.

La solution

Aussi évidente soit-elle que nous ne puissions rien déclarer directement comme privé, il y a encore des choses que nous pouvons utiliser. Un beau concept pratique s’appelle les fermetures. Les fermetures sont expliquées en détail sur un million de blogs, de sites de documentation et d’exemples de code sur Internet, et vous pouvez google les bejesus à votre rythme, donc je ne vais pas me plonger dans l’explication des fermetures ici. Je vais simplement me lancer directement dans leur application pour résoudre le problème d’encapsulation.

Il existe au moins deux façons de gérer cela. Faisons d’abord le plus évident: une fonction d’accès.

Imaginez le monde sans propriétés où vous ne pouvez pas faire d’objet.propriété = valeur (comme vjeko.age = 25 ans dans notre cas, autant que je l’adorerais totalement!). Dans ce monde, vous auriez des fonctions getter et setter:

( ignorez un instant le fait que j’utilise toujours cela.âge pour « encapsuler » la « propriété »)

Évidemment, vous pourriez les appeler comme ceci:

Ensuite, si vous vouliez avoir l’âge en lecture seule, vous supprimeriez simplement la fonction Setter Setter. Si ça.l’âge était vraiment privé (ce qui n’est pas le cas), cela ferait l’affaire pour vous. Le problème est que rien de défini à ce sujet n’est privé, il est accessible à toute personne ayant accès à n’importe quelle instance de l’objet. C’est aussi public que possible.

Pour résoudre ce problème, la première chose à faire est de déplacer la déclaration de fonction getter du prototype vers l’instance. Les membres prototypes sont les plus proches de ce que nous appellerions statique en C #, même si lors de l’exécution, ils ont certains traits de comportement des membres statiques et des membres d’instance. Cependant, éloignons d’abord le membre du prototype et passons à l’instance:

Cela ne résout que la première partie du problème, le fait que getAge a été défini sur le prototype plutôt que sur l’instance. Cependant, avec un simple changement comme celui-ci:

… nous résolvons entièrement le problème:

L’âge est maintenant entièrement encapsulé. Vous pouvez y accéder via la fonction getter, mais vous ne pouvez pas le définir directement car il n’est accessible que dans la portée de fermeture de this.Fonction d’instance de getAge.

Pour implémenter pleinement la classe Person, vous devez déplacer la fonction growOlder du prototype vers l’instance:

Et cela fonctionne exactement comme vous le souhaitez:

Mais pourquoi ça marche ? Cela fonctionne à cause des fermetures. Le paramètre age du constructeur a été capturé dans la portée de fermeture des fonctions getAge et growOlder, vous permettant d’accéder à sa valeur à partir de ces deux fonctions, mais le rendant complètement inaccessible à quiconque, nulle part ailleurs.

Solution encore meilleure

Vous pouvez dire que vous ne voulez pas y accéder via une fonction getter et que vous avez besoin d’une syntaxe de propriété en lecture seule complète. Vous voulez que votre âge soit en lecture seule et encapsulé en même temps. Je t’ai eu! JavaScript ne peut pas encapsuler cela! Sauf qu’il le peut absolument.

J’ai adressé l’objet.Méthode defineProperty dans mon post précédent, et si vous lisez celle-ci, vous pouvez immédiatement voir comment elle peut être appliquée ici.

Définissons donc une propriété d’instance en lecture seule de la classe Person:

Là. Ça n’a pas fait mal. Et ça marche:

Alors voilà, vous l’avez. Encapsulation complète en JavaScript pour vous aider à écrire du bon code isolé et à amener vos compléments de contrôle au niveau réel de kick-a$$.

Heureux JavaScripting, et j’espère avoir plus de temps pour bloguer sur d’autres trucs et astuces JavaScript sympas et utiles pour les développeurs (C /) AL.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.