DEV Community

Alex Chen
Alex Chen

Posted on

JavaScript Map, Set, WeakMap, and WeakSet: When to Use Which

JavaScript Map, Set, WeakMap, and WeakSet: When to Use Which

Stop using plain objects for everything. These built-ins have superpowers.

Map vs Object

// Object — the old way
const obj = {
  name: 'Alex',
  age: 30,
};

obj['name'];           // "Alex"
obj.email = 'a@b.com'; // Add
delete obj.age;        // Remove
Object.keys(obj);      // ['name', 'email']
Object.values(obj);    // ['a@b.com']
Enter fullscreen mode Exit fullscreen mode
// Map — the better way for many cases
const map = new Map();

map.set('name', 'Alex');     // Set value
map.set('age', 30);
map.set(42, 'answer');       // Key can be ANY type (not just string!)
map.set({}, 'object key');   // Even objects as keys!

map.get('name');             // "Alex"
map.has('age');              // true
map.delete('age');           // true
map.size;                    // 2 (no need to manually count!)

// Iteration is easy
for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}

// Insertion order preserved!
const orderedMap = new Map();
orderedMap.set('z', 1);
orderedMap.set('a', 2);
orderedMap.set('m', 3);
[...orderedMap.keys()]; // ['z', 'a', 'm'] — insertion order!

// Object doesn't guarantee order (well, it does now in practice, but Map was designed for it)
Enter fullscreen mode Exit fullscreen mode

When to Use Map vs Object

Use Case Use
String keys only, simple data Object
Need to know size Map (size property)
Non-string keys Map
Frequent add/remove Map
JSON serialization needed Object (Map can't be JSON.stringified directly)
Need insertion order Map
Prototypes matter (hasOwnProperty issues) Map

Set — Unique Values Only

// Array with duplicates
const numbers = [1, 2, 2, 3, 3, 3, 4, 5, 5];

// Create a set (removes duplicates!)
const uniqueSet = new Set(numbers);
uniqueSet;          // Set { 1, 2, 3, 4, 5 }
uniqueSet.size;     // 5

// Convert back to array
[...uniqueSet];     // [1, 2, 3, 4, 5]
Array.from(uniqueSet); // Same thing

// Common use cases:
const arr1 = [1, 2, 3];
const arr2 = [2, 3, 4];
const union = new Set([...arr1, ...arr2]);        // {1,2,3,4}
const intersection = new Set(arr1.filter(x => arr2.includes(x))); // {2,3}
const difference = new Set(arr1.filter(x => !arr2.includes(x)));   // {1}

// Set methods
const set = new Set([1, 2, 3]);
set.add(4);            // Add
set.has(2);            // true (exists?)
set.delete(3);         // true (remove)
set.clear();           // Remove all
set.forEach(x => console.log(x)); // Iterate

// Practical: Track unique visitors
const pageViews = new Set();

function trackVisit(userId) {
  if (!pageViews.has(userId)) {
    console.log(`New unique visitor! Total: ${pageViews.size + 1}`);
  }
  pageViews.add(userId);
}

trackVisit('user_1'); // New unique visitor! Total: 1
trackVisit('user_2'); // New unique visitor! Total: 2
trackVisit('user_1'); // Already visited (no log)
Enter fullscreen mode Exit fullscreen mode

WeakMap — Memory-Friendly Object Metadata

// Problem with regular Map/Objects:
let user = { name: 'Alex' };
const metadata = new Map();
metadata.set(user, { loginCount: 0, lastLogin: null });

user = null; // We're done with user...
// But metadata still holds a reference! → MEMORY LEAK!

// Solution: WeakMap (keys are garbage collected when no other reference exists)
let user2 = { name: 'Sam' };
const weakMetadata = new WeakMap();
weakMetadata.set(user2, { loginCount: 0 });

user2 = null;
// weakMetadata's entry is now eligible for GC!
// No memory leak!

// Practical use case: Private class fields (before # was available)
class User {
  #data = new WeakMap();

  constructor(name) {
    this.#data.set(this, { name, createdAt: Date.now() });
  }

  get name() { return this.#data.get(this).name; }
}

// Another practical use: DOM element metadata
const elementData = new WeakMap();

function attachData(el, data) {
  elementData.set(el, data);
}

function getData(el) {
  return elementData.get(el); // Returns undefined if el removed from DOM
}
Enter fullscreen mode Exit fullscreen mode

WeakSet — Tracking Without Preventing GC

// Similar to WeakMap but for values (not key-value pairs)
// Useful for: "Is this object in my collection?" without preventing GC

const activeElements = new WeakSet();

function activate(element) {
  activeElements.add(element);
  element.classList.add('active');
}

function deactivate(element) {
  activeElements.delete(element);
  element.classList.remove('active');
}

function isActive(element) {
  return activeElements.has(element);
}

// When elements are removed from DOM, they're automatically removed from the WeakSet
// No manual cleanup needed!

// Practical: Instance checking
const instances = new WeakSet();

class Singleton {
  constructor() {
    if (instances.has(Singleton)) {
      throw new Error('Singleton already exists!');
    }
    instances.add(Singleton);
  }
}
Enter fullscreen mode Exit fullscreen mode

Performance Comparison

// For frequent lookups on large datasets:

// Object: Good for string keys, widely supported
// Map: Faster for frequent add/delete, any key type
// Set: O(1) lookups for uniqueness checks
// WeakMap/WeakSet: Slightly slower but prevents memory leaks

// Benchmark example:
const N = 100000;

// Object lookup
const obj = {};
for (let i = 0; i < N; i++) obj[`key_${i}`] = i;
console.time('Object lookup');
for (let i = 0; i < N; i++) obj[`key_${i}`];
console.timeEnd('Object lookup');

// Map lookup
const map = new Map();
for (let i = 0; i < N; i++) map.set(`key_${i}`, i);
console.time('Map lookup');
for (let i = 0; i < N; i++) map.get(`key_${i}`);
console.timeEnd('Map lookup');

// Set has check
const set = new Set();
for (let i = 0; i < N; i++) set.add(i);
console.time('Set has');
for (let i = 0; i < N; i++) set.has(i);
console.timeEnd('Set has');
Enter fullscreen mode Exit fullscreen mode

Quick Reference

Feature Object Map Set WeakMap WeakSet
Key type string/symbol Any Value (any) Object only Object only
Size Manual (keys().length) .size .size No (not enumerable) No
Order Yes (insertion) Yes (insertion) Yes (insertion) No No
Iteration for...in, Object.entries() for...of for...of No No
Serialization JSON.stringify Manual Manual No No
GC safe No No No Yes Yes
Best for Simple data, JSON Dynamic collections Uniqueness Private data Membership tracking

Which one do you use most? Any creative use cases?

Follow @armorbreak for more JavaScript content.

Top comments (0)