weakset sets new es6 javascript ecmascript-6 weakmap

sets - symbol() javascript



ยฟCuรกles son los usos reales de ES6 WeakMap? (5)

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 un Map regular, dado que se habría filtrado, por lo tanto, la principal fortaleza de WeakMap 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 him por compartir)

Fuente completa aquí , se trata de la gestión de los oyentes de los que hablé anteriormente (también puede consultar las specs )

var listenableMap = new WeakMap(); export function getListenable (object) { if (!listenableMap.has(object)) { listenableMap.set(object, {}); } return listenableMap.get(object); } export function getListeners (object, identifier) { var listenable = getListenable(object); listenable[identifier] = listenable[identifier] || []; return listenable[identifier]; } export function on (object, identifier, listener) { var listeners = getListeners(object, identifier); listeners.push(listener); } export function removeListener (object, identifier, listener) { var listeners = getListeners(object, identifier); var index = listeners.indexOf(listener); if(index !== -1) { listeners.splice(index, 1); } } export function emit (object, identifier, ...args) { var listeners = getListeners(object, identifier); for (var listener of listeners) { listener.apply(object, args); } }

¿Cuáles son los usos reales de la estructura de datos WeakMap 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?


๐— ๐—ฒ๐˜๐—ฎ๐—ฑ๐—ฎ๐˜๐—ฎ

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.

๐—ช๐—ถ๐˜๐—ต๐—ผ๐˜‚๐˜ ๐—ช๐—ฒ๐—ฎ๐—ธ๐— ๐—ฎ๐—ฝ๐˜€ ๐—ผ๐—ฟ ๐—ช๐—ฒ๐—ฎ๐—ธ๐—ฆ๐—ฒ๐˜๐˜€:

var elements = document.getElementsByTagName(''*''), i = -1, len = elements.length; while (++i !== len) { // Production code written this poorly makes me want to cry: elements[i].lookupindex = i; elements[i].elementref = []; elements[i].elementref.push( elements[Math.pow(i, 2) % len] ); } // Then, you can access the lookupindex''s // For those of you new to javascirpt, I hope the comments below help explain // how the ternary operator (?:) works like an inline if-statement document.write(document.body.lookupindex + ''<br />'' + ( (document.body.elementref.indexOf(document.currentScript) !== -1) ? // if(document.body.elementref.indexOf(document.currentScript) !== -1){ "true" : // } else { "false" ) // } );

๐—จ๐˜€๐—ถ๐—ป๐—ด ๐—ช๐—ฒ๐—ฎ๐—ธ๐— ๐—ฎ๐—ฝ๐˜€ ๐—ฎ๐—ป๐—ฑ ๐—ช๐—ฒ๐—ฎ๐—ธ๐—ฆ๐—ฒ๐˜๐˜€:

var DOMref = new WeakMap(), __DOMref_value = Array, __DOMref_lookupindex = 0, __DOMref_otherelement = 1, elements = document.getElementsByTagName(''*''), i = -1, len = elements.length, cur; while (++i !== len) { // Production code written this greatly makes me want to ๐Ÿ˜Š: cur = DOMref.get(elements[i]); if (cur === undefined) DOMref.set(elements[i], cur = new __DOMref_value) cur[__DOMref_lookupindex] = i; cur[__DOMref_otherelement] = new WeakSet(); cur[__DOMref_otherelement].add( elements[Math.pow(i, 2) % len] ); } // Then, you can access the lookupindex''s cur = DOMref.get(document.body) document.write(cur[__DOMref_lookupindex] + ''<br />'' + ( cur[__DOMref_otherelement].has(document.currentScript) ? // if(cur[__DOMref_otherelement].has(document.currentScript)){ "true" : // } else { "false" ) // } );

๐—ง๐—ต๐—ฒ ๐——๐—ถ๐—ณ๐—ณ๐—ฒ๐—ฟ๐—ฒ๐—ป๐—ฐ๐—ฒ

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 diferencia importante 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. Math.pow(i, 2) % len] puede parecer un bicho raro que nadie usaría, pero piense 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 liviana que simplemente lo rellenará sin ninguno de los marcos demasiado complejos que puede encontrar en otros rellenos.

~ ¡Feliz codificación!


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. Muestremos esto primero a través de ejemplos, luego explíquelo un poco y finalmente termine con un uso real.

Digamos que estoy usando una API que me da un cierto objeto:

var obj = getObjectFromLibrary();

Ahora, tengo un método que usa el objeto:

function useObj(obj){ doSomethingWith(obj); }

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:

var map = new Map(); // maps can have object keys function useObj(obj){ doSomethingWith(obj); var called = map.get(obj) || 0; called++; // called one more time if(called > 10) report(); // Report called more than 10 times map.set(obj, called); }

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 :

var map = new WeakMap(); // create a weak map function useObj(obj){ doSomethingWith(obj); var called = map.get(obj) || 0; called++; // called one more time if(called > 10) report(); // Report called more than 10 times map.set(obj, called); }

Y la pérdida de memoria se ha ido.

Casos de uso

Algunos casos de uso que de otra forma causarían una pérdida de memoria y que están habilitados por WeakMap incluyen:

  • Mantener datos privados sobre un objeto específico y solo dar acceso a ellos a personas con una referencia al Mapa. Se viene un enfoque más ad-hoc con la propuesta de símbolos privados, pero eso es mucho tiempo a partir de ahora.
  • Mantener datos sobre los objetos de la biblioteca sin cambiarlos o incurrir en gastos generales.
  • Mantener datos sobre un pequeño conjunto de objetos donde existen muchos objetos del tipo para no incurrir en problemas con las clases ocultas que usan los motores JS para objetos del mismo tipo.
  • Mantener datos sobre objetos host como nodos DOM en el navegador.
  • Agregar una capacidad a un objeto desde el exterior (como el ejemplo del emisor de eventos en la otra respuesta).

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 objetos Promise (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 this :

process.on(''unhandledRejection'', function(reason, p) { console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason); // application specific logging, throwing an error, or other logic here });

Mantenemos información sobre las promesas en un mapa y podemos saber cuándo se manejó una promesa rechazada.


Utilizo 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:

// using immutable.js from here https://facebook.github.io/immutable-js/ const memo = new WeakMap(); let myObj = Immutable.Map({a: 5, b: 6}); function someLongComputeFunction (someImmutableObj) { // if we saved the value, then return it if (memo.has(someImmutableObj)) { console.log(''used memo!''); return memo.get(someImmutableObj); } // else compute, set, and return const computedValue = someImmutableObj.get(''a'') + someImmutableObj.get(''b''); memo.set(someImmutableObj, computedValue); console.log(''computed value''); return computedValue; } someLongComputeFunction(myObj); someLongComputeFunction(myObj); someLongComputeFunction(myObj); // reassign myObj = Immutable.Map({a: 7, b: 8}); someLongComputeFunction(myObj);

<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>

Algunas cosas a tener en cuenta:

  • Los objetos Immutable.js devuelven nuevos objetos (con un nuevo puntero) cuando los modifica, por lo que usarlos como claves en un WeakMap garantiza el mismo valor calculado.
  • WeakMap es excelente para los memos porque una vez que el objeto (utilizado como clave) obtiene basura, también lo hace el valor calculado en WeakMap.

WeakMap funciona bien para encapsular y ocultar información

WeakMap solo está disponible para ES6 y superior. Un WeakMap es una colección de pares de clave y valor donde la clave debe ser un objeto. En el siguiente ejemplo, creamos un WeakMap con dos elementos:

var map = new WeakMap(); var pavloHero = {first: "Pavlo", last: "Hero"}; var gabrielFranco = {first: "Gabriel", last: "Franco"}; map.set(pavloHero, "This is Hero"); map.set(gabrielFranco, "This is Franco"); console.log(map.get(pavloHero));//This is Hero

Utilizamos el método set() para definir una asociación entre un objeto y otro elemento (una cadena en nuestro caso). Utilizamos el método get() para recuperar el elemento asociado con un objeto. El aspecto interesante de WeakMap 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 del WeakMap , liberando así memoria.

var TheatreSeats = (function() { var priv = new WeakMap(); var _ = function(instance) { return priv.get(instance); }; return (function() { function TheatreSeatsConstructor() { var privateMembers = { seats: [] }; priv.set(this, privateMembers); this.maxSize = 10; } TheatreSeatsConstructor.prototype.placePerson = function(person) { _(this).seats.push(person); }; TheatreSeatsConstructor.prototype.countOccupiedSeats = function() { return _(this).seats.length; }; TheatreSeatsConstructor.prototype.isSoldOut = function() { return _(this).seats.length >= this.maxSize; }; TheatreSeatsConstructor.prototype.countFreeSeats = function() { return this.maxSize - _(this).seats.length; }; return TheatreSeatsConstructor; }()); })()