CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-memoizee

Complete memoization/caching solution for JavaScript functions with support for any argument types, async/promise functions, cache expiration, and advanced cache management features

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

weakmap-memoization.mddocs/

WeakMap Memoization

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.

Capabilities

WeakMap-Based Memoization

Creates memoized functions that use WeakMap for automatic garbage collection of cache entries tied to object lifetimes.

/**
 * Create WeakMap-based memoized function
 * @param {Function} fn - Function to memoize (first argument must be an object)
 * @param {Object} options - Configuration options (same as regular memoize)
 * @returns {Function} WeakMap-based memoized function
 */
const weakMemoize = require("memoizee/weak");
const memoized = weakMemoize(fn, options);

Usage Examples:

const weakMemoize = require("memoizee/weak");

// Object property analysis
function analyzeObject(obj) {
  console.log("Analyzing object...");
  return {
    keys: Object.keys(obj).length,
    hasNested: Object.values(obj).some(v => typeof v === 'object'),
    firstKey: Object.keys(obj)[0]
  };
}

const memoizedAnalyze = weakMemoize(analyzeObject);

const obj1 = { name: "Alice", age: 30 };
const obj2 = { name: "Bob", age: 25 };

memoizedAnalyze(obj1);  // "Analyzing object...", computes result
memoizedAnalyze(obj1);  // Returns cached result (no console output)
memoizedAnalyze(obj2);  // "Analyzing object...", different object

// When obj1 goes out of scope and is garbage collected,
// its cache entry is automatically removed

WeakMap with Multiple Arguments

Handle functions that take an object as the first argument plus additional parameters.

/**
 * WeakMap memoization with additional arguments beyond the object key
 * The first argument becomes the WeakMap key, remaining args cached per object
 */
const memoized = weakMemoize(function(obj, ...otherArgs) {
  // Function implementation
}, options);

Usage Examples:

const weakMemoize = require("memoizee/weak");

// DOM element processing with options
function processElement(element, options) {
  console.log("Processing element with options:", options);
  return {
    tagName: element.tagName,
    id: element.id,
    processed: true,
    options: options
  };
}

const memoizedProcess = weakMemoize(processElement);

const div1 = document.createElement('div');
const div2 = document.createElement('div');

// Same element, different options - separate cache entries per element
memoizedProcess(div1, { deep: true });   // Computes
memoizedProcess(div1, { deep: false });  // Computes (different options)
memoizedProcess(div1, { deep: true });   // Cache hit

// Different element
memoizedProcess(div2, { deep: true });   // Computes

// When DOM elements are removed from DOM and dereferenced,
// their cache entries are automatically cleaned up

WeakMap with Configuration Options

Combine WeakMap memoization with other memoization features like async support and cache management.

/**
 * WeakMap memoization with advanced options
 * All standard memoization options are supported except global cache operations
 */
const options = {
  async: boolean,           // Async function support
  promise: boolean|string,  // Promise function support
  maxAge: number,          // TTL per object (cleaned up with object)
  max: number,             // Max entries per object
  refCounter: boolean,     // Reference counting per object
  dispose: Function,       // Disposal callback
  // Note: Global clear() is not available due to WeakMap limitations
};

Usage Examples:

const weakMemoize = require("memoizee/weak");

// WeakMap with async support
const asyncWeakMemoized = weakMemoize(async function(obj, query) {
  const response = await fetch(`/api/data/${obj.id}?q=${query}`);
  return response.json();
}, { 
  promise: true,
  maxAge: 60000  // Cache per object for 1 minute
});

// WeakMap with size limiting per object
const limitedWeakMemoized = weakMemoize(function(obj, operation) {
  return performExpensiveOperation(obj, operation);
}, {
  max: 10,  // Max 10 cached results per object
  dispose: (result) => {
    if (result && result.cleanup) result.cleanup();
  }
});

// User session example
class UserSession {
  constructor(userId) {
    this.userId = userId;
    this.loginTime = Date.now();
  }
}

const memoizedUserOperation = weakMemoize(function(session, operation) {
  console.log(`Performing ${operation} for user ${session.userId}`);
  return `${operation}_result_${session.userId}`;
}, {
  maxAge: 300000  // 5 minute cache per session
});

const session1 = new UserSession("123");
memoizedUserOperation(session1, "getData");     // Computes
memoizedUserOperation(session1, "getData");     // Cache hit

// When session1 goes out of scope, its cache is automatically cleaned up

WeakMap-Specific Methods

Delete Operations

Remove cache entries for specific objects and arguments.

/**
 * Delete cache entry for specific object and remaining arguments
 * @param {Object} obj - Object key for WeakMap
 * @param {...any} args - Additional arguments to delete
 */
memoizedFunction.delete(obj, ...args);

Usage Examples:

const weakMemoized = weakMemoize(processData);

const obj = { id: 1 };

weakMemoized(obj, "operation1");
weakMemoized(obj, "operation2");

// Delete specific operation for this object
weakMemoized.delete(obj, "operation1");

// Object still has cache for "operation2"
weakMemoized(obj, "operation2");  // Cache hit
weakMemoized(obj, "operation1");  // Recomputes

Reference Counting with WeakMap

Use reference counting with automatic cleanup when objects are garbage collected.

/**
 * WeakMap with reference counting methods
 */
const options = { refCounter: true };

// Additional methods available when refCounter is enabled
memoizedFunction.deleteRef(obj, ...args);     // Decrement reference
memoizedFunction.getRefCount(obj, ...args);   // Get reference count

Usage Examples:

const weakRefMemoized = weakMemoize(createResource, {
  refCounter: true
});

const obj = { id: "resource-key" };

const resource1 = weakRefMemoized(obj, "config");  // refs: 1
const resource2 = weakRefMemoized(obj, "config");  // refs: 2 (cache hit)

console.log(weakRefMemoized.getRefCount(obj, "config")); // 2

weakRefMemoized.deleteRef(obj, "config");  // refs: 1
weakRefMemoized.deleteRef(obj, "config");  // refs: 0, entry deleted

// Next call creates new resource
const newResource = weakRefMemoized(obj, "config");  // refs: 1

Memory Management Benefits

Automatic Garbage Collection

WeakMap memoization provides automatic memory cleanup when objects are no longer referenced:

function demonstrateGC() {
  const weakMemoized = weakMemoize(expensiveOperation);
  
  // Create objects and cache results
  for (let i = 0; i < 1000; i++) {
    const obj = { id: i, data: new Array(1000).fill(i) };
    weakMemoized(obj, "process");
  }
  
  // All objects go out of scope here
  // Cache entries will be garbage collected automatically
  // No memory leak occurs
}

demonstrateGC();
// Memory is automatically cleaned up

Comparison with Regular Memoization

// Regular memoization - can cause memory leaks
const regularMemoized = memoize(function(obj, operation) {
  return process(obj, operation);
});

// Objects are kept alive by the cache, preventing GC
const objects = [];
for (let i = 0; i < 1000; i++) {
  const obj = { data: new Array(1000).fill(i) };
  objects.push(obj);
  regularMemoized(obj, "process");
}
// objects array and cache both hold references - memory not freed

// WeakMap memoization - automatic cleanup
const weakMemoized = weakMemoize(function(obj, operation) {
  return process(obj, operation);
});

for (let i = 0; i < 1000; i++) {
  const obj = { data: new Array(1000).fill(i) };
  weakMemoized(obj, "process");
  // obj goes out of scope, cache entry eligible for GC
}
// Memory can be freed by garbage collector

Limitations and Considerations

WeakMap Limitations

WeakMap-based memoization has some inherent limitations:

const weakMemoized = weakMemoize(someFunction);

// ❌ Cannot iterate over cached entries
// Object.keys(cache) is not possible

// ❌ Cannot get cache size
// cache.size is not available

// ❌ Cannot clear entire cache globally
// weakMemoized.clear() is not available

// ✅ Can delete entries for specific objects
weakMemoized.delete(specificObject, "arg");

// ✅ Automatic cleanup when objects are GC'd

Best Use Cases

WeakMap memoization is ideal for:

  • DOM element processing: Cache computations tied to DOM elements
  • Object transformation: Cache results tied to specific objects
  • Session-based operations: Cache tied to user sessions or request objects
  • Long-running applications: Prevent memory leaks from accumulated cache
  • Component-based architectures: Cache tied to component instances

Performance Considerations

// WeakMap has some performance characteristics to consider:

// ✅ Good: Object-based keys
const weakMemoized = weakMemoize(fn);
weakMemoized(objectKey, "operation");

// ❌ Avoid: Primitive first arguments (use regular memoize instead)
const regularMemoized = memoize(fn, { primitive: true });
regularMemoized("string-key", "operation");  // More efficient for primitives

// ✅ Good: When objects have natural lifetimes
function processUserSession(session, action) {
  return weakMemoized(session, action);  // Cleanup when session ends
}

// ✅ Good: Prevent memory leaks in long-running apps
const elementProcessor = weakMemoize(processElement);
elements.forEach(el => elementProcessor(el, options));
// Cache cleaned up when elements are removed from DOM

docs

async-functions.md

cache-management.md

function-memoization.md

index.md

method-memoization.md

profiling.md

weakmap-memoization.md

tile.json