Maîtriser l’entretien JavaScript : Qu’est-ce qu’une fermeture ?

« Maîtriser l’entretien JavaScript » est une série de posts destinés à préparer les candidats aux questions courantes qu’ils sont susceptibles de rencontrer lorsqu’ils postulent pour un poste de JavaScript de niveau moyen à supérieur. Il s’agit de questions que j’utilise fréquemment lors de vrais entretiens.

Je lance la série avec la question des 40 000 $. Si vous répondez mal à cette question, il y a de fortes chances que vous ne soyez pas embauché. Si vous êtes embauché, il y a de fortes chances que vous soyez embauché en tant que développeur junior, quelle que soit votre ancienneté en tant que développeur de logiciels. En moyenne, les développeurs juniors sont payés 40k$/an de moins USD que les ingénieurs logiciels plus expérimentés.

Les fermetures sont importantes car elles contrôlent ce qui est et n’est pas dans la portée d’une fonction particulière, ainsi que les variables qui sont partagées entre les fonctions sœurs dans la même portée contenante. Comprendre comment les variables et les fonctions sont liées les unes aux autres est essentiel pour comprendre ce qui se passe dans votre code, dans les styles de programmation fonctionnelle et orientée objet.

La raison pour laquelle rater cette question est si désavantageux lors d’un entretien est que les incompréhensions sur le fonctionnement des closures sont un drapeau rouge assez clair qui peut révéler un manque d’expérience profonde, non seulement en JavaScript, mais dans tout langage qui repose beaucoup sur les closures (Haskell, C#, Python, etc…).

Coder en JavaScript sans comprendre les closures, c’est comme essayer de parler anglais sans comprendre les règles de grammaire – vous arriverez peut-être à faire passer vos idées, mais probablement de manière un peu maladroite.

Vous serez également vulnérable aux malentendus lorsque vous essayerez de comprendre ce que quelqu’un d’autre a écrit.

Non seulement vous devez savoir ce qu’est une fermeture, mais vous devez savoir pourquoi elle est importante et être capable de répondre facilement à plusieurs cas d’utilisation possibles des fermetures.

Les fermetures sont fréquemment utilisées en JavaScript pour la confidentialité des données des objets, dans les gestionnaires d’événements et les fonctions de rappel, ainsi que dans les applications partielles, le currying et d’autres modèles de programmation fonctionnelle.

Si vous ne pouvez pas répondre à cette question, cela pourrait vous coûter le poste, ou ~40k$/an.

Soyez prêt pour un suivi rapide :  » Pouvez-vous nommer deux utilisations courantes des fermetures ? »

Qu’est-ce qu’une fermeture ?

Une fermeture est la combinaison d’une fonction regroupée (enfermée) avec des références à son état environnant (l’environnement lexical). En d’autres termes, une fermeture vous donne accès à la portée d’une fonction externe à partir d’une fonction interne. En JavaScript, les fermetures sont créées chaque fois qu’une fonction est créée, au moment de la création de la fonction.

Pour utiliser une fermeture, définissez une fonction à l’intérieur d’une autre fonction et exposez-la. Pour exposer une fonction, renvoyez-la ou passez-la à une autre fonction.

La fonction interne aura accès aux variables de la portée de la fonction externe, même après le retour de cette dernière.

Utilisation des fermetures (exemples)

Entre autres choses, les fermetures sont couramment utilisées pour donner aux objets la confidentialité des données. La confidentialité des données est une propriété essentielle qui nous aide à programmer sur une interface, et non sur une implémentation. C’est un concept important qui nous aide à construire des logiciels plus robustes, car les détails de mise en œuvre sont plus susceptibles de changer de manière cassante que les contrats d’interface.

« Programmer vers une interface, pas une mise en œuvre. »
Design Patterns : Elements of Reusable Object Oriented Software

En JavaScript, les closures sont le principal mécanisme utilisé pour permettre la confidentialité des données. Lorsque vous utilisez des closures pour la confidentialité des données, les variables enfermées n’ont de portée que dans la fonction (externe) qui les contient. Vous ne pouvez pas accéder aux données à partir d’une portée extérieure, sauf par le biais des méthodes privilégiées de l’objet. En JavaScript, toute méthode exposée définie dans la portée de la fermeture est privilégiée. Par exemple:

Joue avec ceci dans JSBin. (Vous ne voyez pas de sortie ? Copiez et collez ce HTML dans le volet HTML.)

Dans l’exemple ci-dessus, la méthode `.get()` est définie à l’intérieur du scope de `getSecret()`, ce qui lui donne accès à toutes les variables de `getSecret()`, et en fait une méthode privilégiée. Dans ce cas, le paramètre, `secret`.

Les objets ne sont pas la seule façon de produire la confidentialité des données. Les fermetures peuvent également être utilisées pour créer des fonctions à état dont les valeurs de retour peuvent être influencées par leur état interne, par exemple :

const secret = msg => () => msg;

Disponible sur JSBin. (Vous ne voyez pas de sortie ? Copiez et collez ce HTML dans le volet HTML.)

En programmation fonctionnelle, les closures sont fréquemment utilisées pour le curage partiel des applications &. Cela nécessite quelques définitions :

Application : Le processus d’application d’une fonction à ses arguments afin de produire une valeur de retour.

Application partielle : Le processus d’application d’une fonction à certains de ses arguments. La fonction partiellement appliquée est retournée pour une utilisation ultérieure. L’application partielle fixe (applique partiellement la fonction à) un ou plusieurs arguments à l’intérieur de la fonction retournée, et la fonction retournée prend les paramètres restants comme arguments afin de compléter l’application de la fonction.

L’application partielle profite de la portée de la fermeture afin de fixer les paramètres. Vous pouvez écrire une fonction générique qui appliquera partiellement les arguments à la fonction cible. Elle aura la signature suivante :

partialApply(targetFunction: Function, ...fixedArgs: Any) =>
functionWithFewerParams(...remainingArgs: Any)

Si vous avez besoin d’aide pour lire la signature ci-dessus, consultez Rtype : Reading Function Signatures.

Elle prendra une fonction qui prend un nombre quelconque d’arguments, suivie d’arguments que nous voulons appliquer partiellement à la fonction, et retournera une fonction qui prendra les arguments restants.

Un exemple vous aidera. Disons que vous avez une fonction qui ajoute deux nombres :

const add = (a, b) => a + b;

Maintenant vous voulez une fonction qui ajoute 10 à n’importe quel nombre. Nous l’appellerons `add10()`. Le résultat de `add10(5)` devrait être `15`. Notre fonction `partialApply()` peut le faire :

const add10 = partialApply(add, 10);
add10(5);

Dans cet exemple, l’argument, `10` devient un paramètre fixe mémorisé à l’intérieur de la portée de fermeture `add10()`.

Regardons une implémentation possible de `partialApply()`:

Disponible sur JSBin. (Vous ne voyez pas de sortie ? Copiez et collez ce HTML dans le volet HTML.)

Comme vous pouvez le voir, il renvoie simplement une fonction qui conserve l’accès aux arguments `fixedArgs` qui ont été passés dans la fonction `partialApply()`.

Votre tour

Ce billet comporte un billet vidéo d’accompagnement et des devoirs pratiques pour les membres d’EricElliottJS.com. Si vous êtes déjà membre, connectez-vous et entraînez-vous maintenant.

Si vous n’êtes pas membre, inscrivez-vous dès aujourd’hui.

Explorer la série

  • Qu’est-ce qu’une fermeture ?
  • Quelle est la différence entre l’héritage de classe et l’héritage prototypique ?
  • Qu’est-ce qu’une fonction pure ?
  • Qu’est-ce que la composition de fonctions ?
  • Qu’est-ce que la programmation fonctionnelle ?
  • Qu’est-ce qu’une promesse ?
  • Compétences logicielles

Mises à jour :
Juillet 2019 – Introduction clarifiée pour expliquer pourquoi répondre à cette question de manière erronée pourrait vous coûter un emploi ou beaucoup d’argent en salaire.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *