WeakSet and WeakMap in JavaScript: Weak Data Structures
Introduction
WeakSet and WeakMap are data structures introduced in ECMAScript 2015 (ES6) that complement Set and Map, respectively. The main difference is that WeakSet and WeakMap hold “weak” references to their elements, which has important implications for memory management and the lifecycle of objects.
WeakSet
Characteristics of WeakSet
-
It can only contain objects (not primitive values).
-
References to objects in a WeakSet are weak, which means they do not prevent garbage collection if there are no other references to the object.
-
It is not iterable and does not have a
size()method. -
You cannot erase all the content at once (it does not have method
clear()).
Creation and Use of WeakSet
let weakSet = new WeakSet();
let obj1 = {};
let obj2 = {};
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
console.log(weakSet.has(obj2)); // true
weakSet.delete(obj1);
console.log(weakSet.has(obj1)); // false
WeakSet Methods
-
add(value): Add a new object to the WeakSet. -
delete(value): Removes a specific object from the WeakSet. -
has(value): Check if an object is in the WeakSet.
Use Cases of WeakSet
- Mark objects without modifying them:
let visitedSet = new WeakSet();
function visitObject(obj) {
if (visitedSet.has(obj)) {
console.log('Objeto ya visitado');
} else {
visitedSet.add(obj);
console.log('Visitando objeto por primera vez');
}
}
let obj = {};
visitObject(obj); // Visitando objeto por primera vez
visitObject(obj); // Objeto ya visitado
- Prevent duplicates in a collection of objects:
let uniqueObjects = new WeakSet();
function addUniqueObject(collection, obj) {
if (!uniqueObjects.has(obj)) {
uniqueObjects.add(obj);
collection.push(obj);
}
}
let collection = [];
let obj1 = { id: 1 };
let obj2 = { id: 2 };
addUniqueObject(collection, obj1);
addUniqueObject(collection, obj2);
addUniqueObject(collection, obj1); // No se añade, ya existe
console.log(collection.length); // 2
WeakMap
Characteristics of WeakMap
-
Keys must be objects (not primitive values).
-
References to the keys in a WeakMap are weak.
-
It is not iterable and does not have
size()orclear()methods. -
The values can be of any type.
Creation and Use of WeakMap
let weakMap = new WeakMap();
let key1 = {};
let key2 = {};
weakMap.set(key1, 'valor1');
weakMap.set(key2, 'valor2');
console.log(weakMap.get(key1)); // 'valor1'
console.log(weakMap.has(key2)); // true
weakMap.delete(key1);
console.log(weakMap.has(key1)); // false
WeakMap Methods
-
set(key, value): Sets a key-value pair in the WeakMap. -
get(key): Retrieves the value associated with a key. -
delete(key): Remove a specific key-value pair. -
has(key): Check if a key exists in the WeakMap.
Use Cases of WeakMap
- Private data storage:
let privateData = new WeakMap();
class User {
constructor(name, age) {
privateData.set(this, { name, age });
}
getName() {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
}
let user = new User('Alice', 30);
console.log(user.getName()); // 'Alice'
console.log(user.getAge()); // 30
- Object cache with automatic cleaning:
let cache = new WeakMap();
function processObject(obj) {
if (cache.has(obj)) {
return cache.get(obj);
} else {
let result = /* cálculo costoso */;
cache.set(obj, result);
return result;
}
}
let obj1 = {/* ... */};
processObject(obj1); // Calcula y cachea el resultado
processObject(obj1); // Usa el resultado cacheado
// Si obj1 se vuelve inalcanzable, será eliminado del caché automáticamente
Comparison between WeakSet/WeakMap and Set/Map
-
Memory management:
- WeakSet/WeakMap allow objects to be collected by the garbage collector if there are no other references.
- Set/Map maintain strong references, preventing garbage collection.
-
Iterability:
- WeakSet/WeakMap are not iterable.
- Set/Map are iterables and have methods like
forEach().
-
Available methods:
- WeakSet/WeakMap have fewer methods (they don’t have
clear(),size(), etc.). - Set/Map have more methods and properties.
- WeakSet/WeakMap have fewer methods (they don’t have
-
Types of keys/values:
- WeakSet only accepts objects as values.
- WeakMap only accepts objects as keys.
- Set/Map accept any type of value as a key or value.
Practical Examples
- Cache system with automatic cleaning:
let cache = new WeakMap();
function getCachedData(key, fetchData) {
if (cache.has(key)) {
return cache.get(key);
}
let data = fetchData();
cache.set(key, data);
return data;
}
let user = { id: 1 };
let userData = getCachedData(user, () => fetchUserDataFromServer(user.id));
// Cuando 'user' ya no es referenciado, los datos se limpiarán automáticamente del caché
- Event system with automatic listener cleanup:
let listenerMap = new WeakMap();
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = new Set();
}
this.events[event].add(listener);
if (!listenerMap.has(listener)) {
listenerMap.set(listener, new Set());
}
listenerMap.get(listener).add(this);
}
emit(event, ...args) {
if (this.events[event]) {
for (let listener of this.events[event]) {
listener.call(this, ...args);
}
}
}
removeListener(event, listener) {
if (this.events[event]) {
this.events[event].delete(listener);
}
if (listenerMap.has(listener)) {
listenerMap.get(listener).delete(this);
}
}
}
let emitter = new EventEmitter();
let listener = () => console.log('Evento recibido');
emitter.on('myEvent', listener);
emitter.emit('myEvent');
// Si 'listener' se vuelve inalcanzable, se limpiará automáticamente de listenerMap
Performance Considerations
-
WeakSet and WeakMap are useful for preventing memory leaks in certain situations.
-
They are not suitable for cases where you need to iterate over the elements or know the size of the collection.
-
The performance of basic operations (add, delete, has) is similar to Set and Map.
-
The garbage collection of weakly referenced items is not immediate and depends on the JavaScript engine.
Conclusion
WeakSet and WeakMap are specialized data structures in JavaScript that offer a unique way to handle collections of objects with weak references. They are particularly useful in scenarios where it is necessary to associate data with objects without preventing their garbage collection, such as in caching systems, event management, or storage of private data.
Although they have limitations compared to Set and Map, WeakSet and WeakMap provide elegant solutions to specific memory management and object lifecycle problems. Understanding when and how to use these structures can lead to more efficient code and better resource management in complex JavaScript applications.