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