基本上,如果您想跟踪与某个对象相关但由于某种原因不应出现在对象本身上的任何信息,您可以使用地图。
一个简单的例子是跟踪与对象相关的操作完成了多少次。
这是一个演示,我们在不影响动物本身的情况下跟踪每只动物(实例)吃了多少食物:
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`
)
一般来说,创建对象到某些数据的映射的主要原因是您可以维护有关对象的本地信息,这些信息虽然与该对象直接相关,但完全包含在您自己的模块中并且不会污染任何系统的其他部分 (Separation of Concerns)。
另一个例子可能是一个图,它有一个表示节点的对象映射到与它们有连接的其他节点列表(例如在Dijkstra's algorithm 中很有用):
Map<Place, ListOfPlacesICanGoTo>
这允许您通过分离这种关系而不是在对象本身中放置直接的Place.listOfPlaces 链接来拥有更多purePlace 对象。如果在不需要 listOfPlaces 甚至没有意义的其他上下文中使用 Place,这将特别有用。
对象作为类映射结构中的键的另一种常见用法是在使用WeakMap 时,因为它还通过允许每个键对象在没有其他对象引用它时立即被垃圾收集来提高内存效率。
例如,process.on('unhandledRejection') 在节点中的底层实现,uses a WeakMap 跟踪被拒绝但没有错误处理程序在当前滴答内处理拒绝的承诺。
至于使用函数作为键,我个人认为这不太有用,但肯定不是没用的。
一个有用的例子可能是检查某个函数之前是否已经传入,而不是再次调用它,而是返回一个缓存的结果。这可以防止重复执行可能代价高昂的操作。
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
);
与对象一样,出于效率原因,在这些情况下使用WeakMap 也可能更可取。