SKU // 77

JavaScript WeakMap

A WeakMap is to a Map what a WeakSet is to a Set. Its keys must be objects, and it holds them weakly: once nothing else in the program uses a key object, that object can be removed from memory, and its entry disappears from the WeakMap with it.

// The Problem It Solves

Say you want to attach extra data to objects you did not create, like page elements or objects from a library. A regular Map works, but it has a hidden cost:

const clickCounts = new Map();

let button = document.querySelector("#save");
clickCounts.set(button, 0);

button.remove();   // gone from the page...
button = null;
// ...but the Map still holds it, so it can never be freed from memory

(document.querySelector("#save") finds the element on the page whose id is save, and button.remove() takes that element off the page.)

That is a memory leak. In a long-running page that keeps creating and removing elements, the Map grows forever. Swap in a WeakMap and the leak disappears.

// Using a WeakMap

const clickCounts = new WeakMap();

const btnA = { id: "save" };
const btnB = { id: "cancel" };

clickCounts.set(btnA, 0);
clickCounts.set(btnA, clickCounts.get(btnA) + 1);

console.log(clickCounts.get(btnA));   // 1
console.log(clickCounts.has(btnB));   // false
clickCounts.delete(btnA);
1 false

The whole toolkit is set, get, has, and delete.

// The Rules

Map WeakMap
KeysAny valueObjects only
ValuesAny valueAny value
Keeps keys in memoryYesNo
size / loops / clearYesNo
const wm = new WeakMap();
wm.set("name", "Ava");   // TypeError: Invalid value used as weak map key
TypeError: Invalid value used as weak map key

Keys must be objects. (There is one exception: a plain Symbol("id") also works as a key. Symbols are covered in JS Symbols.)

Note
Only the keys are held weakly. Values are held normally, as long as their key is alive. And as with WeakSet, there is no size or loop, because garbage collection could change the answer at any moment.

// Use Case 1: Private Data for Objects

A WeakMap declared inside a module or function can hold data that only that code can reach:

const secrets = new WeakMap();

class Account {
  constructor(owner, pin) {
    this.owner = owner;
    secrets.set(this, { pin });        // not a property of the object
  }
  checkPin(guess) {
    return secrets.get(this).pin === guess;
  }
}

const acct = new Account("Jordan", 4821);
console.log(acct.pin);              // undefined  (not reachable)
console.log(acct.checkPin(4821));   // true
undefined true

When an Account is discarded, its secret entry goes with it automatically. (Newer JavaScript also has #private class fields for this job. This course does not cover them, but WeakMaps were the standard way to do it before they existed.)

// Use Case 2: Caching Results

Remember an expensive calculation for each object, without keeping the objects alive:

const cache = new WeakMap();

function getStats(report) {
  if (cache.has(report)) return cache.get(report);

  const total = report.values.reduce((a, b) => a + b, 0);
  const stats = { total, avg: total / report.values.length };
  cache.set(report, stats);
  return stats;
}

const r = { values: [4, 8, 15, 16] };
console.log(getStats(r));   // calculated
console.log(getStats(r));   // served from the cache
{total: 43, avg: 10.75} {total: 43, avg: 10.75}

// Use Case 3: Counting Visits

A WeakSet can only record that someone visited. A WeakMap can store a count with each visitor:

const visits = new WeakMap();

function track(person) {
  visits.set(person, (visits.get(person) ?? 0) + 1);
}

let paul = { name: "Paul" };
track(paul);
track(paul);
console.log(visits.get(paul));

paul = null;   // the object and its count can now be cleaned up
2

W3Schools' version of this example declares the visitors with const and then sets one to null, which throws TypeError: Assignment to constant variable. Use let for any variable you plan to clear.

// Map, Set, WeakMap, WeakSet: Choosing

A list of unique valuesSet
A lookup table of key to valueMap
Tag objects yes/no, without leaksWeakSet
Attach data to objects, without leaksWeakMap
Rule of Thumb
Start with a Map. Switch to a WeakMap only when the keys are objects whose lifetime you do not control, and you never need to list or count the entries.

// Quick Check

Which one can be a WeakMap key?

  1. { id: 7 }
  2. "id"
  3. 7
  4. true
Show answer
A. Keys must be objects. A primitive key throws TypeError: Invalid value used as weak map key.

In a WeakMap, what is held weakly?

  1. Only the values
  2. Only the keys
  3. Both keys and values
  4. Neither
Show answer
B. Only the keys. A value stays alive as long as its key object does, and goes away with it.

What does this log?

const wm = new WeakMap();
const k = {};
wm.set(k, 1);
wm.set(k, wm.get(k) + 1);
console.log(wm.get(k), wm.size);
Show answer
2 undefined. Setting the same key again replaces its value. WeakMaps have no size.

// Try It

  1. Click Open Playground. Store a value for each object key and read both back
  2. Try using a string as a key and read the error
  3. Open the Account example with its Try it Yourself button and confirm the PIN cannot be read directly
  4. Open the caching example with its Try it Yourself button, add console.log("calculating...") inside getStats, and confirm it only prints once per report
// Video Walkthroughs
Last updated: Oct 10, 2026, 1:42 AM ET