ES6 Map: Почему/когда использовать объект или функцию в качестве ключа?

Я читал, что такое Map, и понимаем различия между Object vs Map. Я не понимаю, почему я использовал objects или functions как ключи, которые разрешает Map.

Вопрос: Почему и когда я когда-либо устанавливал object или function в качестве ключа?

Ответы

Ответ 1

В принципе, если вы хотите отслеживать любую информацию, связанную с объектом, который по какой-то причине не должен присутствовать на самом объекте, вы можете использовать карту.

Простым примером может быть отслеживание того, сколько раз выполнялась операция, относящаяся к объекту.

Здесь демонстрируется, где мы отслеживаем, сколько пищи каждое животное (пример) съели, не затрагивая самого животного:

function Animal(type) {
  this.type = type;
}

// now let 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: function(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`
)

Ответ 2

Если вы пишете функцию, которая выполняет дорогостоящую операцию для копирования/преобразования/обертывания объекта или функции, и вы ожидаете, что функция будет вызываться несколько раз для одних и тех же данных, обычно это улучшение производительности, чтобы сделать предварительную проверку в WeakMap, чтобы убедиться, что вы еще не выполнили дорогостоящую операцию.

Если у вас есть, то вы можете вернуть результат, который уже был рассчитан, что экономит много времени.

Один реальный пример - это утилита, которую я опубликовал под названием di-proxy, но чтобы продемонстрировать свою точку зрения, синтаксис подобен этому (в 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)
  …
})()

Внутри функция createInjector() будет проверять, чтобы убедиться, что она еще не создала функцию-обертку для require. Если он есть, он будет использовать функцию ввода в качестве ключа для WeakMap и вернуть уже созданную оболочку, чтобы сэкономить время:

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())