0
# WeakMap Memoization
1
2
Memory-efficient memoization using WeakMap for garbage collection friendly caching when the first argument is expected to be an object. Cache entries are automatically garbage collected when objects are no longer referenced, preventing memory leaks in long-running applications.
3
4
## Capabilities
5
6
### WeakMap-Based Memoization
7
8
Creates memoized functions that use WeakMap for automatic garbage collection of cache entries tied to object lifetimes.
9
10
```javascript { .api }
11
/**
12
* Create WeakMap-based memoized function
13
* @param {Function} fn - Function to memoize (first argument must be an object)
14
* @param {Object} options - Configuration options (same as regular memoize)
15
* @returns {Function} WeakMap-based memoized function
16
*/
17
const weakMemoize = require("memoizee/weak");
18
const memoized = weakMemoize(fn, options);
19
```
20
21
**Usage Examples:**
22
23
```javascript
24
const weakMemoize = require("memoizee/weak");
25
26
// Object property analysis
27
function analyzeObject(obj) {
28
console.log("Analyzing object...");
29
return {
30
keys: Object.keys(obj).length,
31
hasNested: Object.values(obj).some(v => typeof v === 'object'),
32
firstKey: Object.keys(obj)[0]
33
};
34
}
35
36
const memoizedAnalyze = weakMemoize(analyzeObject);
37
38
const obj1 = { name: "Alice", age: 30 };
39
const obj2 = { name: "Bob", age: 25 };
40
41
memoizedAnalyze(obj1); // "Analyzing object...", computes result
42
memoizedAnalyze(obj1); // Returns cached result (no console output)
43
memoizedAnalyze(obj2); // "Analyzing object...", different object
44
45
// When obj1 goes out of scope and is garbage collected,
46
// its cache entry is automatically removed
47
```
48
49
### WeakMap with Multiple Arguments
50
51
Handle functions that take an object as the first argument plus additional parameters.
52
53
```javascript { .api }
54
/**
55
* WeakMap memoization with additional arguments beyond the object key
56
* The first argument becomes the WeakMap key, remaining args cached per object
57
*/
58
const memoized = weakMemoize(function(obj, ...otherArgs) {
59
// Function implementation
60
}, options);
61
```
62
63
**Usage Examples:**
64
65
```javascript
66
const weakMemoize = require("memoizee/weak");
67
68
// DOM element processing with options
69
function processElement(element, options) {
70
console.log("Processing element with options:", options);
71
return {
72
tagName: element.tagName,
73
id: element.id,
74
processed: true,
75
options: options
76
};
77
}
78
79
const memoizedProcess = weakMemoize(processElement);
80
81
const div1 = document.createElement('div');
82
const div2 = document.createElement('div');
83
84
// Same element, different options - separate cache entries per element
85
memoizedProcess(div1, { deep: true }); // Computes
86
memoizedProcess(div1, { deep: false }); // Computes (different options)
87
memoizedProcess(div1, { deep: true }); // Cache hit
88
89
// Different element
90
memoizedProcess(div2, { deep: true }); // Computes
91
92
// When DOM elements are removed from DOM and dereferenced,
93
// their cache entries are automatically cleaned up
94
```
95
96
### WeakMap with Configuration Options
97
98
Combine WeakMap memoization with other memoization features like async support and cache management.
99
100
```javascript { .api }
101
/**
102
* WeakMap memoization with advanced options
103
* All standard memoization options are supported except global cache operations
104
*/
105
const options = {
106
async: boolean, // Async function support
107
promise: boolean|string, // Promise function support
108
maxAge: number, // TTL per object (cleaned up with object)
109
max: number, // Max entries per object
110
refCounter: boolean, // Reference counting per object
111
dispose: Function, // Disposal callback
112
// Note: Global clear() is not available due to WeakMap limitations
113
};
114
```
115
116
**Usage Examples:**
117
118
```javascript
119
const weakMemoize = require("memoizee/weak");
120
121
// WeakMap with async support
122
const asyncWeakMemoized = weakMemoize(async function(obj, query) {
123
const response = await fetch(`/api/data/${obj.id}?q=${query}`);
124
return response.json();
125
}, {
126
promise: true,
127
maxAge: 60000 // Cache per object for 1 minute
128
});
129
130
// WeakMap with size limiting per object
131
const limitedWeakMemoized = weakMemoize(function(obj, operation) {
132
return performExpensiveOperation(obj, operation);
133
}, {
134
max: 10, // Max 10 cached results per object
135
dispose: (result) => {
136
if (result && result.cleanup) result.cleanup();
137
}
138
});
139
140
// User session example
141
class UserSession {
142
constructor(userId) {
143
this.userId = userId;
144
this.loginTime = Date.now();
145
}
146
}
147
148
const memoizedUserOperation = weakMemoize(function(session, operation) {
149
console.log(`Performing ${operation} for user ${session.userId}`);
150
return `${operation}_result_${session.userId}`;
151
}, {
152
maxAge: 300000 // 5 minute cache per session
153
});
154
155
const session1 = new UserSession("123");
156
memoizedUserOperation(session1, "getData"); // Computes
157
memoizedUserOperation(session1, "getData"); // Cache hit
158
159
// When session1 goes out of scope, its cache is automatically cleaned up
160
```
161
162
## WeakMap-Specific Methods
163
164
### Delete Operations
165
166
Remove cache entries for specific objects and arguments.
167
168
```javascript { .api }
169
/**
170
* Delete cache entry for specific object and remaining arguments
171
* @param {Object} obj - Object key for WeakMap
172
* @param {...any} args - Additional arguments to delete
173
*/
174
memoizedFunction.delete(obj, ...args);
175
```
176
177
**Usage Examples:**
178
179
```javascript
180
const weakMemoized = weakMemoize(processData);
181
182
const obj = { id: 1 };
183
184
weakMemoized(obj, "operation1");
185
weakMemoized(obj, "operation2");
186
187
// Delete specific operation for this object
188
weakMemoized.delete(obj, "operation1");
189
190
// Object still has cache for "operation2"
191
weakMemoized(obj, "operation2"); // Cache hit
192
weakMemoized(obj, "operation1"); // Recomputes
193
```
194
195
### Reference Counting with WeakMap
196
197
Use reference counting with automatic cleanup when objects are garbage collected.
198
199
```javascript { .api }
200
/**
201
* WeakMap with reference counting methods
202
*/
203
const options = { refCounter: true };
204
205
// Additional methods available when refCounter is enabled
206
memoizedFunction.deleteRef(obj, ...args); // Decrement reference
207
memoizedFunction.getRefCount(obj, ...args); // Get reference count
208
```
209
210
**Usage Examples:**
211
212
```javascript
213
const weakRefMemoized = weakMemoize(createResource, {
214
refCounter: true
215
});
216
217
const obj = { id: "resource-key" };
218
219
const resource1 = weakRefMemoized(obj, "config"); // refs: 1
220
const resource2 = weakRefMemoized(obj, "config"); // refs: 2 (cache hit)
221
222
console.log(weakRefMemoized.getRefCount(obj, "config")); // 2
223
224
weakRefMemoized.deleteRef(obj, "config"); // refs: 1
225
weakRefMemoized.deleteRef(obj, "config"); // refs: 0, entry deleted
226
227
// Next call creates new resource
228
const newResource = weakRefMemoized(obj, "config"); // refs: 1
229
```
230
231
## Memory Management Benefits
232
233
### Automatic Garbage Collection
234
235
WeakMap memoization provides automatic memory cleanup when objects are no longer referenced:
236
237
```javascript
238
function demonstrateGC() {
239
const weakMemoized = weakMemoize(expensiveOperation);
240
241
// Create objects and cache results
242
for (let i = 0; i < 1000; i++) {
243
const obj = { id: i, data: new Array(1000).fill(i) };
244
weakMemoized(obj, "process");
245
}
246
247
// All objects go out of scope here
248
// Cache entries will be garbage collected automatically
249
// No memory leak occurs
250
}
251
252
demonstrateGC();
253
// Memory is automatically cleaned up
254
```
255
256
### Comparison with Regular Memoization
257
258
```javascript
259
// Regular memoization - can cause memory leaks
260
const regularMemoized = memoize(function(obj, operation) {
261
return process(obj, operation);
262
});
263
264
// Objects are kept alive by the cache, preventing GC
265
const objects = [];
266
for (let i = 0; i < 1000; i++) {
267
const obj = { data: new Array(1000).fill(i) };
268
objects.push(obj);
269
regularMemoized(obj, "process");
270
}
271
// objects array and cache both hold references - memory not freed
272
273
// WeakMap memoization - automatic cleanup
274
const weakMemoized = weakMemoize(function(obj, operation) {
275
return process(obj, operation);
276
});
277
278
for (let i = 0; i < 1000; i++) {
279
const obj = { data: new Array(1000).fill(i) };
280
weakMemoized(obj, "process");
281
// obj goes out of scope, cache entry eligible for GC
282
}
283
// Memory can be freed by garbage collector
284
```
285
286
## Limitations and Considerations
287
288
### WeakMap Limitations
289
290
WeakMap-based memoization has some inherent limitations:
291
292
```javascript
293
const weakMemoized = weakMemoize(someFunction);
294
295
// ❌ Cannot iterate over cached entries
296
// Object.keys(cache) is not possible
297
298
// ❌ Cannot get cache size
299
// cache.size is not available
300
301
// ❌ Cannot clear entire cache globally
302
// weakMemoized.clear() is not available
303
304
// ✅ Can delete entries for specific objects
305
weakMemoized.delete(specificObject, "arg");
306
307
// ✅ Automatic cleanup when objects are GC'd
308
```
309
310
### Best Use Cases
311
312
WeakMap memoization is ideal for:
313
314
- **DOM element processing**: Cache computations tied to DOM elements
315
- **Object transformation**: Cache results tied to specific objects
316
- **Session-based operations**: Cache tied to user sessions or request objects
317
- **Long-running applications**: Prevent memory leaks from accumulated cache
318
- **Component-based architectures**: Cache tied to component instances
319
320
### Performance Considerations
321
322
```javascript
323
// WeakMap has some performance characteristics to consider:
324
325
// ✅ Good: Object-based keys
326
const weakMemoized = weakMemoize(fn);
327
weakMemoized(objectKey, "operation");
328
329
// ❌ Avoid: Primitive first arguments (use regular memoize instead)
330
const regularMemoized = memoize(fn, { primitive: true });
331
regularMemoized("string-key", "operation"); // More efficient for primitives
332
333
// ✅ Good: When objects have natural lifetimes
334
function processUserSession(session, action) {
335
return weakMemoized(session, action); // Cleanup when session ends
336
}
337
338
// ✅ Good: Prevent memory leaks in long-running apps
339
const elementProcessor = weakMemoize(processElement);
340
elements.forEach(el => elementProcessor(el, options));
341
// Cache cleaned up when elements are removed from DOM
342
```