The Map and WeakMap abstractions are capable of storing key-value pairs where, unlike regular objects, the key can be anything, including non-primitive values:
const populationBySpecies = new Map();
const reindeer = { name: 'Reindeer', formalName: 'Rangifer tarandus' };
populationBySpecies.set(reindeer, 2000000);
populationBySpecies.get(reindeer); // => 2,000,000
WeakMap is similar to Map, but it only holds a weak reference to the object that's used as a key, meaning that, if the object becomes unavailable due to being garbage-collected elsewhere in your program, then WeakMap will cease to keep a hold of it.
Most of the time, a plain object is all you will need. You should only reach for Map or WeakMap if you need your keys to be non-primitive or if you want to weakly hold your values.