Quelles sont les utilisations réelles de la WeakMap
structure de données introduite dans ECMAScript 6?
Étant donné qu'une clé d'une carte faible crée une référence forte à sa valeur correspondante, garantissant qu'une valeur qui a été insérée dans une carte faible ne disparaîtra jamais tant que sa clé est toujours vivante, elle ne peut pas être utilisée pour les tableaux de mémos, caches ou toute autre chose pour laquelle vous utiliseriez normalement des références faibles, des cartes avec des valeurs faibles, etc. pour.
Il me semble que ceci:
weakmap.set(key, value);
... est juste une façon détournée de dire ceci:
key.value = value;
Quels cas concrets me manquent?
javascript
ecmascript-6
weakmap
valderman
la source
la source
WeakMap
s peut être utilisé pour détecter les fuites de mémoire: stevehanov.ca/blog/?id=148Réponses:
Fondamentalement
WeakMaps fournit un moyen d'étendre des objets de l'extérieur sans interférer avec la récupération de place. Chaque fois que vous souhaitez étendre un objet mais ne le pouvez pas car il est scellé - ou à partir d'une source externe - un WeakMap peut être appliqué.
Un WeakMap est une carte (dictionnaire) où les clés sont faibles - c'est-à-dire, si toutes les références à la clé sont perdues et qu'il n'y a plus de références à la valeur - la valeur peut être récupérée. Voyons cela d'abord à travers des exemples, puis expliquons-le un peu et terminons enfin avec une utilisation réelle.
Disons que j'utilise une API qui me donne un certain objet:
Maintenant, j'ai une méthode qui utilise l'objet:
Je veux garder une trace du nombre de fois que la méthode a été appelée avec un certain objet et signaler si elle se produit plus de N fois. Naïvement, on pourrait penser à utiliser une carte:
Cela fonctionne, mais il y a une fuite de mémoire - nous gardons maintenant une trace de chaque objet de bibliothèque transmis à la fonction qui empêche les objets de bibliothèque d'être récupérés. Au lieu de cela - nous pouvons utiliser un
WeakMap
:Et la fuite de mémoire a disparu.
Cas d'utilisation
Certains cas d'utilisation qui autrement provoqueraient une fuite de mémoire et sont activés par
WeakMap
s incluent:Regardons une utilisation réelle
Il peut être utilisé pour étendre un objet de l'extérieur. Donnons un exemple pratique (adapté, en quelque sorte réel - pour faire un point) du monde réel de Node.js.
Disons que vous êtes les Node.js et vous avez des
Promise
objets - maintenant vous voulez garder une trace de toutes les promesses actuellement rejetées - cependant, vous ne pas voulez les empêcher d'être des déchets collectés dans le cas où aucune référence existent pour eux.Maintenant, vous ne voulez pas ajouter de propriétés aux objets natifs pour des raisons évidentes - vous êtes donc bloqué. Si vous gardez des références aux promesses, vous causez une fuite de mémoire car aucune récupération de place ne peut se produire. Si vous ne conservez pas de références, vous ne pouvez pas enregistrer d'informations supplémentaires sur les promesses individuelles. Tout schéma impliquant la sauvegarde de l'ID d'une promesse signifie que vous avez besoin d'une référence à celle-ci.
Entrez WeakMaps
WeakMaps signifie que les touches sont faibles. Il n'y a aucun moyen d'énumérer une carte faible ou d'obtenir toutes ses valeurs. Dans une carte faible, vous pouvez stocker les données en fonction d'une clé et lorsque la clé est récupérée, les valeurs font de même.
Cela signifie que, étant donné une promesse, vous pouvez stocker l'état à ce sujet - et cet objet peut toujours être récupéré. Plus tard, si vous obtenez une référence à un objet, vous pouvez vérifier si vous avez un état le concernant et le signaler.
Cela a été utilisé pour mettre en œuvre des crochets de rejet non gérés par Petka Antonov comme ceci :
Nous conservons des informations sur les promesses sur une carte et pouvons savoir quand une promesse rejetée a été traitée.
la source
useObj
exemple utilisant unMap
et non unWeakMap
nous utilisons l'objet passé comme clé de carte. L'objet n'est jamais supprimé de la carte (car nous ne saurions pas quand le faire), il y a donc toujours une référence et il ne peut jamais être récupéré. Dans l'exemple WeakMap dès que toutes les autres références à l'objet ont disparu - l'objet peut être effacé duWeakMap
. Si vous n'êtes toujours pas sûr de ce que je veux dire, faites-le moi savoircalled
exemple est mieux écrit en utilisant jsfiddle.net/f2efbm7z et il ne démontre pas l'utilisation d'une carte faible. En fait, il peut être mieux écrit de 6 façons au total , que je vais énumérer ci-dessous.p[key_symbol] = data
. ou 2) une dénomination unique;p.__key = data
. ou 3) portée privée;(()=>{let data; p.Key = _=>data=_;})()
. ou 4) proxy avec 1 ou 2 ou 3. ou 5) remplacer / étendre la classe Promise par 1 ou 2 ou 3. ou 6) remplacer / étendre la classe Promise avec un tuple de membres nécessaires. - Dans tous les cas, une carte faible n'est pas nécessaire, sauf si vous avez besoin d'un cache sensible à la mémoire.Cette réponse semble biaisée et inutilisable dans un scénario réel. Veuillez le lire tel quel et ne le considérez pas comme une option réelle pour autre chose que l'expérimentation
Un cas d'utilisation pourrait être de l'utiliser comme dictionnaire pour les auditeurs, j'ai un collègue qui l'a fait. C'est très utile car tout auditeur est directement ciblé sur cette façon de faire les choses. Au revoir
listener.on
.Mais d'un point de vue plus abstrait,
WeakMap
est particulièrement puissant pour dématérialiser l'accès à pratiquement tout, vous n'avez pas besoin d'un espace de noms pour isoler ses membres car il est déjà impliqué par la nature de cette structure. Je suis presque sûr que vous pourriez apporter des améliorations majeures à la mémoire en remplaçant les clés d'objet redondantes maladroites (même si la déconstruction fait le travail pour vous).Avant de lire la suite
Je me rends compte maintenant que mon accent n'est pas exactement la meilleure façon de résoudre le problème et souligné Benjamin Gruenbaum (consultez sa réponse, si elle n'est pas déjà au-dessus de la mienne: p), ce problème n'aurait pas pu être résolu avec un habitué
Map
, car il aurait fui, donc la principale force deWeakMap
est qu'il n'interfère pas avec la collecte des ordures étant donné qu'ils ne gardent pas de référence.Voici le code actuel de mon collègue (merci à lui pour le partage)
La source complète ici , c'est la gestion des auditeurs dont j'ai parlé ci-dessus (vous pouvez également jeter un œil aux spécifications )
la source
WeakMap
fonctionne bien pour l'encapsulation et la dissimulation d'informationsWeakMap
est uniquement disponible pour ES6 et supérieur. AWeakMap
est une collection de paires clé et valeur où la clé doit être un objet. Dans l'exemple suivant, nous créons unWeakMap
avec deux éléments:Nous avons utilisé la
set()
méthode pour définir une association entre un objet et un autre élément (une chaîne dans notre cas). Nous avons utilisé laget()
méthode pour récupérer l'élément associé à un objet. L'aspect intéressant duWeakMap
s est le fait qu'il contient une faible référence à la clé à l'intérieur de la carte. Une référence faible signifie que si l'objet est détruit, le garbage collector supprimera l'entrée entière de laWeakMap
, libérant ainsi de la mémoire.la source
𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮
Les cartes faibles peuvent être utilisées pour stocker des métadonnées sur les éléments DOM sans interférer avec la récupération de place ou rendre vos collègues fous de votre code. Par exemple, vous pouvez les utiliser pour indexer numériquement tous les éléments d'une page Web.
𝗪𝗶𝘁𝗵𝗼𝘂𝘁 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗼𝗿 𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:
𝗨𝘀𝗶𝗻𝗴 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗮𝗻𝗱 𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:
𝗧𝗵𝗲 𝗗𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝗰𝗲
La différence peut sembler négligeable, à part le fait que la version de carte faible est plus longue, mais il existe une différence majeure entre les deux morceaux de code illustrés ci-dessus. Dans le premier extrait de code, sans cartes faibles, le morceau de code stocke les références dans tous les sens entre les éléments DOM. Cela empêche les éléments DOM d'être récupérés.
(i * i) % len
peut sembler bizarre que personne n'utiliserait, mais détrompez-vous: beaucoup de code de production a des références DOM qui rebondissent sur tout le document. Maintenant, pour le deuxième morceau de code, car toutes les références aux éléments sont faibles, lorsque vous supprimez un nœud, le navigateur est en mesure de déterminer que le nœud n'est pas utilisé (impossible à atteindre par votre code), et supprimez-le donc de la mémoire. La raison pour laquelle vous devriez vous préoccuper de l'utilisation de la mémoire et des ancres de mémoire (des choses comme le premier extrait de code où les éléments inutilisés sont conservés en mémoire) est parce que plus d'utilisation de la mémoire signifie plus de tentatives de GC du navigateur (pour essayer de libérer de la mémoire pour éviter un crash du navigateur) signifie une expérience de navigation plus lente et parfois un crash du navigateur.Quant à un polyfill pour ceux-ci, je recommanderais ma propre bibliothèque ( trouvée ici @ github ). Il s'agit d'une bibliothèque très légère qui la remplira simplement sans aucun des cadres trop complexes que vous pourriez trouver dans d'autres polyfills.
~ Bon codage!
la source
elements
à null et vous avez terminé: Ce sera GCed. & Re "Les références DOM qui rebondissent sur tout le document ", peu importe: une fois le lien principalelements
disparu, toutes les références circulaires seront GCées. Si votre élément contient des références à un élément dont il n'a pas besoin, corrigez le code et définissez la référence sur null lorsque vous avez fini de l'utiliser. Ce sera GCed. Pas besoin de cartes faibles .elements
de null ne permettra pas au navigateur de GC les éléments dans la première situation d'extrait. Cela est dû au fait que vous définissez des propriétés personnalisées sur les éléments, puis ces éléments peuvent toujours être obtenus, et leurs propriétés personnalisées sont toujours accessibles, empêchant ainsi l'un d'eux d'être GC'ed. Pensez-y comme une chaîne d'anneaux métalliques. Solongas vous avez accès à au moins un maillon de la chaîne, vous pouvez vous accrocher à ce maillon de la chaîne, et ainsi empêcher toute la chaîne d'objets de tomber dans l'abîme.j'utilise
WeakMap
pour le cache de mémorisation sans souci des fonctions qui prennent en paramètres des objets immuables.La mémorisation est une façon élégante de dire "après avoir calculé la valeur, mettez-la en cache pour ne pas avoir à la recalculer".
Voici un exemple:
Afficher l'extrait de code
Quelques points à noter:
la source
J'ai ce cas d'utilisation / exemple simple basé sur des fonctionnalités pour WeakMaps.
GÉRER UNE COLLECTION D'UTILISATEURS
J'ai commencé avec un
User
objet dont les propriétés comprennent unfullname
,username
,age
,gender
et une méthode appeléeprint
qui imprime un résumé lisible par l' homme des autres propriétés.J'ai ensuite ajouté une carte appelée
users
pour conserver une collection de plusieurs utilisateurs qui sont saisis parusername
.L'ajout de la collection a également nécessité des fonctions d'aide pour ajouter, obtenir, supprimer un utilisateur et même une fonction pour imprimer tous les utilisateurs par souci d'exhaustivité.
Avec tout le code ci-dessus exécuté, par exemple NodeJS , seule la
users
carte a la référence aux objets utilisateur dans l'ensemble du processus. Il n'y a aucune autre référence aux objets utilisateur individuels.Exécuter ce code un shell NodeJS interactif, tout comme un exemple, j'ajoute quatre utilisateurs et les imprime:
AJOUTER PLUS D'INFO AUX UTILISATEURS SANS MODIFIER LE CODE EXISTANT
Supposons maintenant qu'une nouvelle fonctionnalité soit requise dans laquelle les liens de chaque utilisateur de la plateforme de médias sociaux (SMP) doivent être suivis avec les objets utilisateur.
La clé ici est également que cette fonctionnalité doit être implémentée avec une intervention minimale sur le code existant.
Ceci est possible avec WeakMaps de la manière suivante.
J'ajoute trois WeakMaps distincts pour Twitter, Facebook, LinkedIn.
Une fonction d'assistance
getSMPWeakMap
est ajoutée simplement pour renvoyer le WeakMap associé au nom SMP donné.Une fonction pour ajouter un lien SMP d'utilisateurs au SMP WeakMap donné.
Une fonction pour imprimer uniquement les utilisateurs présents sur le SMP donné.
Vous pouvez maintenant ajouter des liens SMP pour les utilisateurs, également avec la possibilité pour chaque utilisateur d'avoir un lien sur plusieurs SMP.
... en continuant avec l'exemple précédent, j'ajoute des liens SMP aux utilisateurs, plusieurs liens pour les utilisateurs Bill et Sarah, puis j'imprime les liens pour chaque SMP séparément:
Supposons maintenant qu'un utilisateur soit supprimé de la
users
carte en appelantdeleteUser
. Cela supprime la seule référence à l'objet utilisateur. À son tour, cela effacera également le lien SMP de tout ou partie des WeakMaps SMP (par Garbage Collection) car sans l'objet utilisateur, il n'y a aucun moyen d'accéder à l'un de ses liens SMP.... en poursuivant l'exemple, je supprime l'utilisateur Bill , puis j'imprime les liens des SMP auxquels il était associé:
Aucun code supplémentaire n'est requis pour supprimer individuellement le lien SMP séparément et le code existant avant que cette fonctionnalité n'ait été modifiée de toute façon.
S'il existe un autre moyen d'ajouter cette fonctionnalité avec / sans WeakMaps, n'hésitez pas à commenter.
la source