/ javascript

JavaScript Map and WeakMap

This is the second part of a two post series. The first one was JavaScript Set and WeakSet.

Map

From MDN: The Map object is a simple key/value map. Any value (both objects and primitive values) may be used as either a key or a value.

Maps are like regular Objects if you treat them as a dictionary (key-value map). But if you notice, anything can be a key on a Map, even objects or other maps.

Just like the Set, the Map implementation stores their elements in insertion order. So, for..of loop will return a [key, value] for every iteration in that order.

var DoctorEleven = {}; 
var SonicScrewdriverEleven = {};

var DoctorsSonicScrewdriver = new Map();
DoctorsSonicScrewdriver.set(DoctorEleven, SonicScrewdriverEleven);

var myMap = new Map();
myMap.set(NaN, "Oh!");
myMap.set(0, "zero");
myMap.set(1, "one");

for (var [key, value] of myMap) {
  console.log(key + " = " + value);
}

Maps are like Objects, and of course they are since we been using Objects to store values as the were Maps since the beginning. So, why change now?

  • Object keys have to always be strings, where they can be any type of value on Map.

  • You can easily get the amount of elements in your Map, in Object you will have to create that functionality for every instance.

  • Equality is been check using the SameValueZero comparison algorithm.

  • There are default keys in an Object since their prototype. Map doesn't inherit this behaviour, making them a little "cleaner". Map.prototype has the attributes Writable, Enumerable and Configurable set to false to ensure this.

The Map constructor

In the ECMAScript Specification, explains that Maps are designed to be subclassable. Meaning that you can inherit one Map from another.

You can set an iterator on the Map constructor. The iterator has to produce an two elements structure (like array of arrays). The first element will be the key for the Map element, and the second his value.

var mapA = new Map();
mapA.set('hey', 'you!');

console.log(mapA.get('hey'));
>> 'you!'

var mapB = new Map(mapA);
console.log(mapB.get('hey'));
>> 'you!'

Another option could be to simply pass an array. Like [[key1, val1], [key2, val2], ...].

var mapB = new Map([['hey', 'you!']]);
console.log(mapB.get('hey'));
>> 'you!'

The last example will produce exactly the same results as the previous one.

List of methods and properties

Name Description
size Return the number of elements in the Map.
add(key, value) Sets the value for the key in the Map object. Returns the Map object.
get(key) Returns the value associated to the key, or undefined if there is none.
clear() Removes all elements.
delete(value) Removes the element associated.
entries() Returns a new Iterator object that contains an array of [key, value] for each element in the Map object in insertion order.
forEach(callback) Regular forEach.
has(value) Returns true if the key is in the Map, false if it isn't.
values() Returns a new Iterator object with the values of each element in the Map.
keys() Returns a new Iterator object that contains the keys for each element in the Map object in insertion order.

WeakMap

Analogous to the WeakSet, the WeakMap keys can only be Object. Primitive data types aren't allowed here.

References to the objects key are weakly attached. This won't prevent the garbage collector to delete them if they're only referenced here.

Thanks to this, the keys on the WeakMap are not iterable. If you'd like a list of the keys, you'll have to store it by yourself.

The value of .length will always be 0.

List of methods and properties

Name Description
delete(key) Removes any value associated to the key.
has(key) Returns true if the key exists on the Map or false if it doesn't.
get(key) Returns the value of the key or undefined.
set(key, value) Sets the value for the key in the WeakMap object. Returns the WeakMap object.

the-end-is-near-good

Cool uses

Nick Fitzgerald introduces a techinque for hiding private implementations using WeakMaps in his blog.
He saids that "The WeakMap privates pattern is the best choice when you really need to hide private implementation details from public API consumers."