javascript ecmascript-6

javascript - Mapa ES6: ¿Por qué/cuándo usar un objeto o una función como clave?



ecmascript-6 (3)

Básicamente, si desea realizar un seguimiento de cualquier información relacionada con un objeto que, por algún motivo, no esté presente en el objeto en sí, puede usar un mapa.

Un ejemplo simple podría ser rastrear cuántas veces se realizó una operación relacionada con un objeto.

Aquí hay una demostración donde hacemos un seguimiento de la cantidad de comida que cada animal (instancia) ha comido sin afectar al animal en sí:

function Animal(type) { this.type = type; } // now let''s say somewhere else in our program // we want to have a farm where animals can eat. // We want the farm to keep track of how much each animal ate but // the animal itself doesn''t need to know how much food it has eaten. const AnimalFarm = (() => { const mapOfAnimalToAmountOfFood = new Map(); return { feedAnimal(animal, amountOfFood) { // if the animal is being fed the first time // initialize the amount to 0 if (!mapOfAnimalToAmountOfFood.has(animal)) { mapOfAnimalToAmountOfFood.set(animal, 0) } // add amountOfFood to the amount of food already eaten mapOfAnimalToAmountOfFood.set( animal, mapOfAnimalToAmountOfFood.get(animal) + amountOfFood ) }, getAmountEaten: function(animal) { return mapOfAnimalToAmountOfFood.get(animal) } } })() const dog1 = new Animal(''dog'') const dog2 = new Animal(''dog'') AnimalFarm.feedAnimal(dog1, 300) AnimalFarm.feedAnimal(dog1, 500) AnimalFarm.feedAnimal(dog2, 1234) console.log( `dog1 ate ${AnimalFarm.getAmountEaten(dog1)} total` ) console.log( `dog2 ate ${AnimalFarm.getAmountEaten(dog2)} total` )

En general, una de las principales razones para crear un mapa de objetos para algunos datos es que puede mantener información local sobre un objeto que, aunque está directamente relacionado con este objeto, está completamente contenido en su propio módulo y no contamina ninguna otra parte de El sistema ( Separación de inquietudes ).

Otro ejemplo podría ser un gráfico que tenga un mapa de objetos que representan los nodos a una lista de otros nodos a los que tienen conexión (útil, por ejemplo, en el algoritmo de Dijkstra ):

Map<Place, ListOfPlacesICanGoTo>

Esto le permite tener un objeto Place más pure al separar esta relación en lugar de colocar un enlace Place.listOfPlaces directo dentro del objeto en sí. Esto es particularmente útil si un Place se usa en otros contextos donde listOfPlaces no es necesario o incluso no tiene sentido.

Otro uso común de los objetos como claves en una estructura similar a un mapa es cuando se utiliza un WeakMap , porque también es más eficiente en la memoria al permitir que cada objeto clave se recoja en la basura tan pronto como nada más lo haga referencia. Un ejemplo podría ser la implementación subyacente para process.on(''unhandledRejection'') en el nodo que utiliza un WeakMap para realizar un seguimiento de las promesas que fueron rechazadas, pero ningún controlador de errores se ocupó del rechazo dentro del tick actual.

En lo que respecta al uso de una función como clave, personalmente creo que esto es menos útil pero ciertamente no es inútil.

Un ejemplo útil podría ser verificar si una determinada función ya se había pasado antes y no invocarla nuevamente, pero devolver un resultado en caché. Esto podría evitar la ejecución repetitiva de operaciones potencialmente costosas.

const map = new Map(); function invokeOrGetFromCache(fn) { if (map.has(fn)) { return map.get(fn); } const result = fn(); map.set(fn, result); return result; } function exampleFn() { console.log(''start''); for (i = 0; i < 100000; i++); console.log(''done''); return true; } console.log( invokeOrGetFromCache(exampleFn) // runs exampleFn ); console.log( invokeOrGetFromCache(exampleFn) // retrieves from cache );

Al igual que con los objetos, usar un WeakMap podría ser preferible en estas situaciones también, por razones de eficiencia.

He leído qué es un Map y entiendo las diferencias entre Objeto y Mapa. Lo que no entiendo es por qué usaría objects o functions como las teclas que permite un Map .

Pregunta: ¿Por qué y cuándo establecería un object o una function como clave?


Si escribe una función que ejecuta una operación costosa para copiar / transformar / envolver un objeto o una función, y espera que se llame a la función varias veces para los mismos datos, normalmente es una mejora de rendimiento realizar una verificación WeakMap en un WeakMap para asegurarse de que aún no ha ejecutado la operación costosa.

Si lo ha hecho, entonces puede devolver el resultado que ya se ha calculado, lo que ahorra mucho tiempo.

Un ejemplo del mundo real es una utilidad que he publicado llamada di-proxy , pero para demostrar mi punto, la sintaxis es algo como esto (en Node.js):

const createInjector = require(''di-proxy'') // pass dependency resolver to injector factory const inject = createInjector(require) // wrap IIFE with dependency injector inject(({ http, express, ''socket.io'': sio }) => { const app = express() const server = http.Server(app) const io = sio(server) … })()

Internally , la función createInjector() se verificará para asegurarse de que no haya generado una función de envoltura para require . Si lo ha hecho, usará la función de entrada como una clave para un WeakMap y devolverá la función de envoltura que ya generó, para ahorrar tiempo:

function createInjector (require, noCache = false) { … // if require function is weakly referenced and memoization is enabled if (!noCache && this.has(require)) { // return cached injector return this.get(require) } … // expensive operation to generate cached injector … // weakly reference injector with require function as key this.set(require, inject) // return wrapped function return inject }.bind(new WeakMap())


para casos de prueba por ejemplo:

const test= new Map(); test.set(''isZero'', (v) => console.log(v === 0)); test.set(''isNumber'', (v) => console.log(typeof v === ''number'')); const value = 10; for(let [key, fn] of test){ // console.warn(key); fn(value); }