¿Cuáles son los usos reales de la WeakMap
estructura de datos introducida en ECMAScript 6?
Dado que una clave de un mapa débil crea una referencia fuerte a su valor correspondiente, asegurando que un valor que se ha insertado en un mapa débil nunca desaparecerá mientras su clave siga viva, no se puede usar para tablas de notas, cachés o cualquier otra cosa para la que normalmente usaría referencias débiles, mapas con valores débiles, etc.
Me parece que esto:
weakmap.set(key, value);
... es solo una forma indirecta de decir esto:
key.value = value;
¿Qué casos de uso concretos me estoy perdiendo?
javascript
ecmascript-6
weakmap
valderman
fuente
fuente
WeakMap
s se puede usar para detectar pérdidas de memoria: stevehanov.ca/blog/?id=148Respuestas:
Fundamentalmente
WeakMaps proporciona una forma de extender objetos desde el exterior sin interferir con la recolección de basura. Siempre que desee extender un objeto pero no pueda porque está sellado, o desde una fuente externa, se puede aplicar un WeakMap.
Un WeakMap es un mapa (diccionario) donde las claves son débiles, es decir, si se pierden todas las referencias a la clave y no hay más referencias al valor, el valor se puede recolectar basura. Vamos a mostrar esto primero a través de ejemplos, luego explicarlo un poco y finalmente terminar con un uso real.
Digamos que estoy usando una API que me da un cierto objeto:
Ahora, tengo un método que usa el objeto:
Quiero hacer un seguimiento de cuántas veces se llamó al método con un determinado objeto e informar si sucede más de N veces. Ingenuamente uno pensaría usar un Mapa:
Esto funciona, pero tiene una pérdida de memoria: ahora hacemos un seguimiento de cada objeto de biblioteca que se pasa a la función que evita que los objetos de la biblioteca se recojan basura. En cambio, podemos usar un
WeakMap
:Y la pérdida de memoria se ha ido.
Casos de uso
Algunos casos de uso que de otro modo causarían una pérdida de memoria y están habilitados por
WeakMap
s incluyen:Veamos un uso real
Se puede usar para extender un objeto desde el exterior. Pongamos un ejemplo práctico (adaptado, más o menos real, para destacar) del mundo real de Node.js.
Digamos que usted es Node.js y tiene
Promise
objetos, ahora desea realizar un seguimiento de todas las promesas rechazadas actualmente; sin embargo, no desea evitar que se recojan basura en caso de que no existan referencias a ellos.Ahora, no desea agregar propiedades a objetos nativos por razones obvias, por lo que está atascado. Si mantiene referencias a las promesas, está causando una pérdida de memoria ya que no se puede recolectar basura. Si no mantiene referencias, no puede guardar información adicional sobre promesas individuales. Cualquier esquema que implique guardar la identificación de una promesa inherentemente significa que necesita una referencia a ella.
Ingrese WeakMaps
WeakMaps significa que las teclas son débiles. No hay formas de enumerar un mapa débil o de obtener todos sus valores. En un mapa débil, puede almacenar los datos basados en una clave y cuando la clave se recolecta basura, también lo hacen los valores.
Esto significa que, dada una promesa, puede almacenar el estado al respecto, y ese objeto aún puede ser recolectado. Más adelante, si obtiene una referencia a un objeto, puede verificar si tiene algún estado relacionado con él e informarlo.
Esto fue utilizado para implementar ganchos de rechazo no manejados por Petka Antonov como este :
Mantenemos información sobre las promesas en un mapa y podemos saber cuándo se manejó una promesa rechazada.
fuente
useObj
ejemplo usando aMap
y no aWeakMap
usamos el objeto pasado como clave de mapa. El objeto nunca se elimina del mapa (ya que no sabríamos cuándo hacerlo), por lo que siempre hay una referencia a él y nunca se puede recolectar basura. En el ejemplo de WeakMap, tan pronto como todas las demás referencias al objeto hayan desaparecido, el objeto se puede borrar deWeakMap
. Si todavía no está seguro de lo que quiero decir, hágamelo sabercalled
ejemplo está mejor escrito usando jsfiddle.net/f2efbm7z y no demuestra el uso de un mapa débil. De hecho, se puede escribir mejor en un total de 6 formas, que enumeraré a continuación.p[key_symbol] = data
. o 2) nombres únicos;p.__key = data
. o 3) ámbito privado;(()=>{let data; p.Key = _=>data=_;})()
. o 4) proxy con 1 o 2 o 3. o 5) reemplazar / extender la clase Promise con 1 o 2 o 3. o 6) reemplazar / extender la clase Promise con una tupla de miembros necesarios. - En cualquier caso, no se necesita un mapa débil a menos que necesite una memoria caché sensible a la memoria.Esta respuesta parece estar sesgada e inutilizable en un escenario del mundo real. Léalo como está y no lo considere como una opción real para otra cosa que no sea la experimentación.
Un caso de uso podría ser usarlo como un diccionario para los oyentes, tengo un compañero de trabajo que hizo eso. Es muy útil porque cualquier oyente está directamente dirigido con esta forma de hacer las cosas. Adiós
listener.on
.Pero desde un punto de vista más abstracto,
WeakMap
es especialmente poderoso para desmaterializar el acceso a básicamente cualquier cosa, no necesita un espacio de nombres para aislar a sus miembros ya que ya está implícito en la naturaleza de esta estructura. Estoy bastante seguro de que podría hacer algunas mejoras importantes en la memoria al reemplazar las claves de objeto redundantes incómodas (aunque la deconstrucción hace el trabajo por usted).Antes de leer lo que sigue
Ahora me doy cuenta de que mi énfasis no es exactamente la mejor manera de abordar el problema y, como señaló Benjamin Gruenbaum (vea su respuesta, si aún no está por encima de la mía: p), este problema no podría haberse resuelto con regularidad
Map
, ya que se habría filtrado, por lo tanto, la principal fortalezaWeakMap
es que no interfiere con la recolección de basura dado que no mantienen una referencia.Aquí está el código real de mi compañero de trabajo (gracias a él por compartir)
Fuente completa aquí , se trata de la gestión de los oyentes que mencioné anteriormente (también puede consultar las especificaciones )
fuente
WeakMap
funciona bien para encapsular y ocultar informaciónWeakMap
solo está disponible para ES6 y superior. AWeakMap
es una colección de pares de clave y valor donde la clave debe ser un objeto. En el siguiente ejemplo, construimos unWeakMap
con dos elementos:Utilizamos el
set()
método para definir una asociación entre un objeto y otro elemento (una cadena en nuestro caso). Utilizamos elget()
método para recuperar el elemento asociado con un objeto. El aspecto interesante de laWeakMap
s es el hecho de que tiene una referencia débil a la clave dentro del mapa. Una referencia débil significa que si el objeto se destruye, el recolector de basura eliminará toda la entrada delWeakMap
, liberando así la memoria.fuente
𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮
Los mapas débiles se pueden usar para almacenar metadatos sobre elementos DOM sin interferir con la recolección de basura o hacer que los compañeros de trabajo se enojen con su código. Por ejemplo, podría usarlos para indexar numéricamente todos los elementos de una página web.
𝗪𝗶𝘁𝗵𝗼𝘂𝘁 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗼𝗿 𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:
𝗨𝘀𝗶𝗻𝗴 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗮𝗻𝗱 𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:
𝗧𝗵𝗲 𝗗𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝗰𝗲
La diferencia puede parecer insignificante, aparte del hecho de que la versión de mapa débil es más larga, sin embargo, hay una gran diferencia entre las dos piezas de código que se muestran arriba. En el primer fragmento de código, sin mapas débiles, el fragmento de código almacena referencias en todos los sentidos entre los elementos DOM. Esto evita que los elementos DOM se recojan basura.
(i * i) % len
Puede parecer un bicho raro que nadie usaría, pero piénselo de nuevo: un montón de código de producción tiene referencias DOM que rebotan en todo el documento. Ahora, para el segundo fragmento de código, debido a que todas las referencias a los elementos son débiles, cuando elimina un nodo, el navegador puede determinar que el nodo no se utiliza (su código no puede alcanzarlo), y así eliminarlo de la memoria. La razón por la que debería preocuparse por el uso de la memoria y los anclajes de la memoria (cosas como el primer fragmento de código donde se guardan los elementos no utilizados en la memoria) es porque más uso de la memoria significa más intentos de GC del navegador (para intentar liberar memoria para evitar un bloqueo del navegador) significa una experiencia de navegación más lenta y, a veces, un bloqueo del navegador.En cuanto a un polyfill para estos, recomendaría mi propia biblioteca (que se encuentra aquí @ github ). Es una biblioteca muy ligera que simplemente lo rellenará sin ninguno de los marcos demasiado complejos que puede encontrar en otros rellenos polivinílicos.
~ ¡Feliz codificación!
fuente
elements
en nulo y listo: se aplicará GC. & Re "Las referencias DOM que rebotan en todo el documento ", no importan en absoluto: una vez que el enlace principalelements
desaparezca, toda la referencia circular será GC. Si su elemento retiene referencias a elementos que no necesita, entonces arregle el código y configure la referencia como nula cuando haya terminado de usarlo. Será GCed. No se necesitan mapas débiles .elements
en nulo no permitirá que el navegador GC los elementos en la primera situación de fragmento. Esto se debe a que establece propiedades personalizadas en los elementos, y luego esos elementos aún se pueden obtener, y sus propiedades personalizadas aún se pueden acceder, evitando así que cualquiera de ellos sea GC'ed. Piense en ello como una cadena de anillos de metal. Si tiene acceso a al menos un eslabón de la cadena, puede mantener ese eslabón en la cadena y evitar así que toda la cadena de elementos caiga al abismo.Lo uso
WeakMap
para el caché de la memorización sin preocupaciones de funciones que toman objetos inmutables como su parámetro.La memorización es una forma elegante de decir "después de calcular el valor, almacénelo en caché para que no tenga que volver a calcularlo".
Aquí hay un ejemplo:
Mostrar fragmento de código
Algunas cosas a tener en cuenta:
fuente
Tengo este caso / ejemplo de uso basado en características simples para WeakMaps.
GESTIONAR UNA COLECCIÓN DE USUARIOS
Me comenzó con un
User
objeto cuyas propiedades incluir unafullname
,username
,age
,gender
y un método llamadoprint
que imprime un resumen legible por humanos de las otras propiedades.Luego agregué un Mapa llamado
users
para mantener una colección de múltiples usuarios que están codificados porusername
.La adición de la Colección también requería funciones auxiliares para agregar, obtener, eliminar un Usuario e incluso una función para imprimir a todos los usuarios en aras de la integridad.
Con todo el código anterior ejecutándose, digamos NodeJS , solo el
users
Mapa tiene la referencia a los Objetos de usuario en todo el proceso. No hay otra referencia a los Objetos de usuario individuales.Ejecutando este código un shell interactivo de NodeJS, como ejemplo, agrego cuatro usuarios e los imprimo:
AGREGAR MÁS INFORMACIÓN A LOS USUARIOS SIN MODIFICAR EL CÓDIGO EXISTENTE
Ahora supongamos que se requiere una nueva función en la que los enlaces de la Plataforma de redes sociales (SMP) de cada usuario deben rastrearse junto con los Objetos de usuario.
La clave aquí también es que esta característica debe implementarse con una intervención mínima al código existente.
Esto es posible con WeakMaps de la siguiente manera.
Agrego tres WeakMaps separados para Twitter, Facebook, LinkedIn.
Se
getSMPWeakMap
agrega una función auxiliar simplemente para devolver el WeakMap asociado con el nombre SMP dado.Una función para agregar un enlace SMP de usuarios al WeakMap SMP dado.
Una función para imprimir solo los usuarios que están presentes en el SMP dado.
Ahora puede agregar enlaces SMP para los usuarios, también con la posibilidad de que cada usuario tenga un enlace en múltiples SMP.
... continuando con el ejemplo anterior, agrego enlaces SMP a los usuarios, enlaces múltiples para los usuarios Bill y Sarah y luego imprimo los enlaces para cada SMP por separado:
Ahora digamos que un usuario se elimina del
users
mapa llamandodeleteUser
. Eso elimina la única referencia al objeto de usuario. Esto a su vez también borrará el enlace SMP de cualquiera / todos los WeakMaps SMP (por Garbage Collection) ya que sin el objeto de usuario no hay forma de acceder a ninguno de sus enlaces SMP.... continuando con el Ejemplo, elimino al usuario Bill y luego imprimo los enlaces de los SMP con los que estaba asociado:
No se requiere ningún código adicional para eliminar individualmente el enlace SMP por separado y el código existente antes de esta función no se modificó de ninguna manera.
Si hay alguna otra forma de agregar esta función con / sin WeakMaps, no dude en comentar.
fuente