Working with ES6 Collections: Set, Map, and Weak References
Set Fundamentals
Sets store unique values of any primitive type or object reference. The constructor automatically filters duplicates using the SameValueZero comparison algorithm, which treats NaN values as identical and distinguishes between positive and negative zero.
const activeSessions = new Set();
activeSessions.add('session-001');
activeSessions.add('session-002');
activeSessions.add('session-001'); // Ignored as duplicate
console.log(activeSessions.size); // 2
The API provides chainable add(), boolean-returning delete(), existence-checking has(), and clear() methods for bulk removal.
const featureFlags = new Set()
.add('dark-mode')
.add('beta-api')
.add('experimental-ui');
featureFlags.delete('experimental-ui');
console.log(featureFlags.has('dark-mode')); // true
featureFlags.clear();
console.log(featureFlags.size); // 0
Iterating Set Values
Sets implement the iterable protocol. While keys() and values() return identical iterators (since values serve as their own keys), entries() yields [value, value] tuples for destructuring compatibility with Map interfaces.
const dependencies = new Set(['lodash', 'react', 'typescript']);
for (const pkg of dependencies.values()) {
console.log(`Installing: ${pkg}`);
}
for (const [key, value] of dependencies.entries()) {
console.log(`${key} <=> ${value}`);
}
dependencies.forEach((value, key, setRef) => {
console.log(`${key}: ${value}`);
});
Array Deduplication Pattern
Converting between Sets and Arrays leverages the spread operator to eliminate duplicate primitives efficiently:
const telemetryIds = [101, 102, 102, 103, 104, 104, 105];
const uniqueIds = [...new Set(telemetryIds)];
console.log(uniqueIds); // [101, 102, 103, 104, 105]
WeakSet for Transient Object Tracking
WeakSet maintains weak referneces to objects exclusively—attempting to store primitives throws a TypeError. When an object loses all external references, it becomes eligible for garbage collection regardless of WeakSet membership, preventing memory leaks in long-running applications.
const domNodes = new WeakSet();
let buttonRef = document.getElementById('submit-btn');
domNodes.add(buttonRef);
console.log(domNodes.has(buttonRef)); // true
buttonRef = null;
// The DOM node may be garbage collected; WeakSet does not prevent cleanup
WeakSet deliberately lacks enumeration capabilities. No size property, forEach() method, or iteration protocols exist, ensuring that garbage collection remains opaque and non-deterministic. Typical use cases involve marking objects as processed or validated without extending their lifetime.
Map Collections
Maps store keyed data where keys may be arbitrary values—including objects, functions, or primitives. Unlike objects, Maps preserve insertion order and use SameValueZero for key identity, allowing distinct entries for 5 and '5'.
const userCache = new Map();
const userObject = { id: 42 };
userCache.set(userObject, { name: 'Alex', role: 'developer' });
userCache.set('guest', { name: 'Anonymous' });
console.log(userCache.get(userObject)); // { name: 'Alex', ... }
The set() method returns the Map instance for chaining, while get() returns undefined for missing keys. Use has() to distinguish between undefined values and absent keys.
const config = new Map([
['api-endpoint', 'https://api.service.io'],
['request-timeout', 30000],
['max-retries', 5]
]);
console.log(config.get('max-retries')); // 5
console.log(config.has('debug-mode')); // false
config.delete('request-timeout');
console.log(config.size); // 2
Map Iteration and Destructuring
Maps provide direct iteration over [key, value] pairs via the entries() method (also the default ietrator):
const headers = new Map([
['Content-Type', 'application/json'],
['Authorization', 'Bearer token123'],
['X-Request-ID', 'uuid-v4']
]);
for (const [header, value] of headers) {
console.log(`${header}: ${value}`);
}
headers.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
WeakMap for Private Member Simulation
WeakMap requires object keys and maintains weak references to those keys. This makes WeakMap ideal for associating private data with class instances or DOM nodes with out exposing properties publicly or preventing garbage collection.
const internalState = new WeakMap();
class DataProcessor {
constructor(apiKey) {
internalState.set(this, {
key: apiKey,
requestCount: 0
});
}
async fetchData(endpoint) {
const state = internalState.get(this);
state.requestCount++;
return fetch(`${endpoint}?key=${state.key}`);
}
getMetrics() {
return internalState.get(this).requestCount;
}
}
const processor = new DataProcessor('secret-key-xyz');
// When processor is dereferenced, the WeakMap entry disappears automatically
WeakMap exposes only get(), set(), has(), and delete() methods. The absence of size, clear(), and iteration methods ensures that key garbage collection remains unpredictable and efficient. Select WeakMap when managing auxiliary data for objects with finite lifecycles; choose Map when you require key enumeration or persistent storage regardless of external references.