I’d guess that over 70% of JavaScript developers have only used objects for the collecting and maintaining the data in their projects. Well, it’s actually so true that the newer collection objects, such as Map
and Set
, are way underused, even though they came out in 2015.
So today, I’m going to talk about the fantastic new features from 2015 — Map
, Set
, WeakMap
, and WeakSet
.
We should talk about how to use objects first.
Well, I’m sure over 90% of you already know this part since you clicked this article in order to get to know the new collection objects, but for beginners of JavaScript, let’s briefly talk about them.
const algorithm = { site: "leetcode" };
console.log(algorithm.site); // leetcode
for (const key in algorithm) {
console.log(key, algorithm[key]);
}
// site leetcode
delete algorithm.site;
console.log(algorithm.site); // undefined
So I made an algorithm
object whose key and value are a string-type value. And I’ve checked the value successfully by approaching it with the .
keyword.
Also, a for-in
loop is good for looping through the object. You can access the value corresponding to its key with the []
keyword. But a for-of
loop can’t be used because objects aren’t iterable.
The property of objects can be removed with the delete
keyword. This completely gets rid of the property from the object, and you should be careful not to be confused with this approach.
const algorithm = { site: "leetcode" };
// Property is not removed!!
algorithm.site = undefined;
// Property is removed!!
delete algorithm.site;
algorithm.site = undefined
just assigns the new value to site
.
OK, we’ve quickly discussed a few things about objects:
A Map
is a new collection object in JavaScript that functions like an object. But there are a few primary differences compared to a regular object.
Firstly, let’s take a look at a simple example creating a Map
object.
const map = new Map();
// Map(0) {}
That’s it. Map
doesn’t require anything to be created. But the way you can add data is slightly different.
map.set('name', 'john');
// Map(1) {"name" => "john"}
Map
has a special method for adding a property in it called set
. It takes two arguments: the key as the first argument and the value as the second.
map.set('phone', 'iPhone');
// Map(2) {"name" => "john", "phone" => "iPhone"}
map.set('phone', 'iPhone');
// Map(2) {"name" => "john", "phone" => "iPhone"}
However, it doesn’t allow you to add the existing data in it. If the value corresponding to the key of the new data has already existed in the Map
object, the new data won’t be added.
map.set('phone', 'Galaxy');
// Map(2) {"name" => "john", "phone" => "Galaxy"}
But you can overwrite the existing data with a different value.
Map
is an iterable object, which means it can be mapped over with a for-of
statement.
for (const item of map) {
console.dir(item);
}
// Array(2) ["name", "john"]
// Array(2) ["phone", "Galaxy"]
One thing to remember is Map
gives you data as an array form. You should destructure the array or access each index to get the key or the value.
To get the keys or the values only, there are methods for you to follow as well.
map.keys();
// MapIterator {"name", "phone"}
map.values();
// MapIterator {"john", "Galaxy"}
map.entries();
// MapIterator {"name" => "john", "phone" => "Galaxy"}
You can even use the spread operator to get the full data of Map
because the spread operator also works with iterable objects behind the scene.
const simpleSpreadedMap = [...map];
// [Array(2), Array(2)]
It’s also very easy to remove data from the Map
object. All you need to do is call delete
.
map.delete('phone');
// true
map.delete('fake');
// false
The delete
returns the boolean, which indicates whether or not the deleting function successfully deleted the data. If yes, it returns true
, and otherwise, it returns false
.
WeakMap
originated from Map
, so they are very similar to each other. However, WeakMap
has one big difference.
How does WeakMap
get its name? Well, it’s because its connection or relation to the data object its reference link refers to isn’t as strong as Map
’s connection or relation, making it weak.
So what does this exactly mean?
const John = { name: 'John' };
const weakMap = new WeakMap();
weakMap.set(John, 'student');
// WeakMap {{...} => "student"}
weakMap.set('john', 'student');
// Uncaught TypeError: Invalid value used as weak map key
You could pass any value as the key into the Map
object, but WeakMap
is different. It only accepts an object as the key; otherwise, it returns an error.
The methods you can use with WeakMap
are as follows.
delete
get
has
set
The big difference in this topic is that WeakMap
doesn’t support the methods for iterating the object. But why? It’s described down below.
This is the biggest difference compared to Map
.
let John = { major: "math" };
const map = new Map();
const weakMap = new WeakMap();
map.set(John, 'John');
weakMap.set(John, 'John');
John = null;
/* John is garbage-collected */
When the John
object is garbage-collected, the Map
object keeps holding the reference link, while the WeakMap
object loses the link. So when you use WeakMap
, you should consider this feature.
Set
is also quite similar to Map
, but Set
is more useful for a single value.
const set = new Set();
set.add(1);
set.add('john');
set.add(BigInt(10));
// Set(4) {1, "john", 10n}
Like Map
, Set
also prevents us from adding the same value.
set.add(5);
// Set(1) {5}
set.add(5);
// Set(1) {5}
Since Set
is an iterable object, you can use a for-of
or forEach
statement.
for (const val of set) {
console.dir(val);
}
// 1
// 'John'
// 10n
// 5
set.forEach(val => console.dir(val));
// 1
// 'John'
// 10n
// 5
This part is exactly the same as Map
’s deletion. It returns true
if the data is successfully removed; otherwise, it returns false
.
set.delete(5);
// true
set.delete(function(){});
// false;
Set
could be quite useful when you don’t want to add the same values into the array form.
/* With Set */
const set = new Set();
set.add(1);
set.add(2);
set.add(2);
set.add(3);
set.add(3);
// Set {1, 2, 3}
// Converting to Array
const arr = [ ...set ];
// [1, 2, 3]
Object.prototype.toString.call(arr);
// [object Array]
/* Without Set */
const hasSameVal = val => ar.some(v === val);
const ar = [];
if (!hasSameVal(1)) ar.push(1);
if (!hasSameVal(2)) ar.push(2);
if (!hasSameVal(3)) ar.push(3);
Like WeakMap
, WeakSet
also loses the access link to inner data if they’re garbage-collected.
let John = { major: "math" };
const set = new Set();
const weakSet = new WeakSet();
set.add(John);
// Set {{...}}
weakSet.add(John);
// WeakSet {{...}}
John = null;
/* John is garbage-collected */
Once the object,John
is garbage-collected, WeakSet
has no way to access its data that was referencing John
. And WeakSet
doesn’t support for-of
or forEach
since it’s not iterable.
WeakMap
only accepts the object as the key, while Map
doesn’tfor-of
, forEach
, or spread
operator is supportedStill, many teams or companies don’t use Map
s or Set
s, I think. Maybe it’s because they don’t feel it’s necessary or because arrays still can do almost everything they want.
However, Map
s or Set
s could be very unique and powerful things in JavaScript, depending on each situation. So I hope one day, you could have a chance to use them.
☞ JavaScript Programming Tutorial Full Course for Beginners
☞ Learn JavaScript - Become a Zero to Hero
☞ Javascript Project Tutorial: Budget App
☞ E-Commerce JavaScript Tutorial - Shopping Cart from Scratch