Door 20 | JS Adventskalender
Skip to content

Door 20

Published: at 05:00 AMSuggest Changes

WeakMap and WeakSet – Managing Memory Efficiently

JavaScript has offered powerful data structures like Map and Set since ES6 to efficiently represent key-value pairs or sets of values. But sometimes you need structures that store objects as keys or values without preventing the garbage collector from releasing unused objects. This is where WeakMap and WeakSet come into play. These data structures allow you to reference objects “weakly”. This means that an object accessed only through a WeakMap or WeakSet can be cleaned up by the garbage collector as soon as there are no other references to it.

What are WeakMap and WeakSet?

These weak references prevent memory leaks when you want to temporarily hold objects in a data structure for specific purposes (e.g., for internal marking, caching, or access control) without permanently blocking memory.

Important Properties

  1. No Enumeration:
    Unlike Map or Set, WeakMap and WeakSet have no methods to read all contained keys or values (no keys(), values(), entries() methods). This is because the content can dynamically change when the garbage collector removes objects.

  2. Keys and Values Must Be Objects:
    WeakMap keys and WeakSet elements must be of type object. Primitive values (String, Number, Boolean, etc.) are not allowed.

  3. Automatic Memory Release:
    When no other reference to an object exists, it disappears in the background from the WeakMap or WeakSet without you having to do anything.

Simple Examples

WeakMap Example:

const wm = new WeakMap();

let obj = { name: 'Max' };
wm.set(obj, 'Data for Max');

console.log(wm.get(obj)); // 'Data for Max'

// If we now remove the only reference to obj:
obj = null; 
// At some point, the garbage collector will remove the entry from the WeakMap
// (Not immediately observable, but happens in the background)

WeakSet Example:

const ws = new WeakSet();

let user = { id: 1, name: 'Anna' };
ws.add(user);

console.log(ws.has(user)); // true

user = null;
// At some point, the object is removed from the WeakSet by the garbage collector

Practical Use Cases

1. Private Data for Objects:

const privateData = new WeakMap();

class User {
  constructor(name, password) {
    this.name = name;
    // Store password privately
    privateData.set(this, { password });
  }

  checkPassword(input) {
    const data = privateData.get(this);
    return data.password === input;
  }
}

const user = new User('Max', 'secret123');
console.log(user.name); // 'Max'
console.log(user.password); // undefined
console.log(user.checkPassword('secret123')); // true

2. Caching:

const cache = new WeakMap();

function processData(obj) {
  if (cache.has(obj)) {
    console.log('Returning cached result');
    return cache.get(obj);
  }

  console.log('Computing result');
  const result = expensiveOperation(obj);
  cache.set(obj, result);
  return result;
}

function expensiveOperation(obj) {
  return obj.value * 2;
}

const data = { value: 42 };
console.log(processData(data)); // Computing result, 84
console.log(processData(data)); // Returning cached result, 84

3. Tracking Visited Objects:

const visited = new WeakSet();

function traverse(obj) {
  if (visited.has(obj)) {
    console.log('Already visited');
    return;
  }

  visited.add(obj);
  console.log('Visiting:', obj.name);

  // Process children...
}

const node1 = { name: 'Node 1' };
const node2 = { name: 'Node 2' };

traverse(node1); // Visiting: Node 1
traverse(node1); // Already visited
traverse(node2); // Visiting: Node 2

4. DOM Element Metadata:

const elementData = new WeakMap();

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

function getData(element) {
  return elementData.get(element);
}

const button = document.querySelector('#myButton');
attachData(button, { clicks: 0, lastClicked: null });

// When button is removed from DOM and no longer referenced,
// the data is automatically garbage collected

Advantages

Limitations

Conclusion

WeakMap and WeakSet are specialized data structures that excel in specific scenarios where you need to associate data with objects without preventing garbage collection. They are particularly useful for caching, storing private data, and tracking object states without creating memory leaks.

Use WeakMap and WeakSet in your next project when you need to associate temporary data with objects without managing their lifecycle manually!


Previous Post
Door 21
Next Post
Door 19