Complete memoization/caching solution for JavaScript functions with support for any argument types, async/promise functions, cache expiration, and advanced cache management features
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Specialized utilities for memoizing object methods with lazy property descriptors and proper this context handling. Ideal for prototype method optimization and instance-specific caching where methods need to be memoized on a per-instance basis.
Create lazy property descriptors for memoizing object methods with proper this binding and instance-specific caching.
/**
* Create memoized method property descriptors
* @param {Object} methods - Object mapping method names to descriptors
* @returns {Object} Lazy property descriptors for Object.defineProperties
*/
const memoizeMethods = require("memoizee/methods");
const descriptors = memoizeMethods(methodsObject);Usage Examples:
const memoizeMethods = require("memoizee/methods");
const d = require("d"); // Property descriptor helper
class DataProcessor {
constructor(data) {
this.data = data;
this.processed = false;
}
}
// Define memoized methods on prototype
Object.defineProperties(DataProcessor.prototype, memoizeMethods({
// Basic memoized method
calculateSum: d(function() {
console.log("Computing sum...");
return this.data.reduce((sum, val) => sum + val, 0);
}),
// Memoized method with options
expensiveTransform: d(function(transformType) {
console.log(`Performing ${transformType} transform...`);
return this.data.map(val => {
switch(transformType) {
case 'double': return val * 2;
case 'square': return val * val;
default: return val;
}
});
}, { maxAge: 60000 }), // Cache for 1 minute
// Async memoized method
fetchRelatedData: d(function(endpoint, callback) {
console.log(`Fetching from ${endpoint}...`);
setTimeout(() => {
callback(null, { endpoint, data: `data-for-${this.data[0]}` });
}, 100);
}, { async: true })
}));
// Usage
const processor1 = new DataProcessor([1, 2, 3, 4, 5]);
const processor2 = new DataProcessor([10, 20, 30]);
processor1.calculateSum(); // "Computing sum...", returns 15
processor1.calculateSum(); // Cache hit, returns 15 (no console output)
processor2.calculateSum(); // "Computing sum...", returns 60 (different instance)
processor2.calculateSum(); // Cache hit for processor2
// Each instance has its own cache
processor1.expensiveTransform('double'); // Computed for processor1
processor2.expensiveTransform('double'); // Computed for processor2 (different cache)Configure memoized methods with complex options including custom normalizers and argument handling.
/**
* Advanced method memoization options
*/
const descriptors = memoizeMethods({
methodName: d(function(...args) {
// Method implementation
}, {
// Standard memoization options
length: number,
primitive: boolean,
maxAge: number,
max: number,
async: boolean,
promise: boolean,
// Method-specific options
getNormalizer: function(length) {
// Return custom normalizer function
return function(args) {
return customKeyGeneration(args);
};
}
})
});Usage Examples:
const memoizeMethods = require("memoizee/methods");
const d = require("d");
class ApiClient {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
}
Object.defineProperties(ApiClient.prototype, memoizeMethods({
// Custom normalizer for complex object arguments
fetchUserData: d(function(userQuery) {
console.log("Fetching user data...");
return fetch(`${this.baseUrl}/users`, {
method: 'POST',
body: JSON.stringify(userQuery),
headers: { 'Authorization': `Bearer ${this.apiKey}` }
}).then(r => r.json());
}, {
promise: true,
maxAge: 300000, // 5 minute cache
getNormalizer: function(length) {
return function(args) {
// Normalize based on query content, not object identity
return JSON.stringify(args[0]);
};
}
}),
// Method with argument resolvers
processNumbers: d(function(num1, num2, operation) {
console.log(`Processing: ${num1} ${operation} ${num2}`);
switch(operation) {
case 'add': return num1 + num2;
case 'multiply': return num1 * num2;
default: return 0;
}
}, {
length: 3,
resolvers: [Number, Number, String], // Type coercion
maxAge: 30000
}),
// Async method with size limiting
heavyComputation: d(function(input, callback) {
console.log("Starting heavy computation...");
setTimeout(() => {
const result = input.map(x => x ** 2).reduce((a, b) => a + b, 0);
callback(null, result);
}, 1000);
}, {
async: true,
max: 50, // Limit cache size per instance
dispose: (result) => {
console.log("Disposing computation result:", result);
}
})
}));
// Usage
const client = new ApiClient("https://api.example.com", "key123");
client.fetchUserData({ name: "Alice", active: true });
client.fetchUserData({ active: true, name: "Alice" }); // Cache hit (normalized)
client.processNumbers("10", "5", "add"); // 15 (with type coercion)
client.processNumbers(10, 5, "add"); // Cache hitEach object instance maintains its own separate cache for memoized methods.
/**
* Instance isolation - each object gets its own method cache
* Methods are memoized per instance, not globally
*/Usage Examples:
const memoizeMethods = require("memoizee/methods");
const d = require("d");
class Calculator {
constructor(name) {
this.name = name;
this.calculations = 0;
}
}
Object.defineProperties(Calculator.prototype, memoizeMethods({
fibonacci: d(function(n) {
this.calculations++;
console.log(`${this.name}: Computing fib(${n})`);
if (n < 2) return n;
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
})
}));
const calc1 = new Calculator("Calculator A");
const calc2 = new Calculator("Calculator B");
// Each instance has separate cache
calc1.fibonacci(10); // Computes and caches for calc1
calc2.fibonacci(10); // Computes and caches for calc2 (separate cache)
calc1.fibonacci(10); // Cache hit for calc1
calc2.fibonacci(10); // Cache hit for calc2
console.log(`${calc1.name} calculations: ${calc1.calculations}`);
console.log(`${calc2.name} calculations: ${calc2.calculations}`);
// Cache management per instance
calc1.fibonacci.clear(); // Clears only calc1's cache
calc2.fibonacci.delete(10); // Deletes only from calc2's cacheAccess cache management methods on individual instances.
/**
* Cache management methods available on each instance
*/
instance.methodName.delete(...args); // Delete specific cache entry
instance.methodName.clear(); // Clear all cached results for this instance
instance.methodName._get(...args); // Get cached value without execution
instance.methodName._has(...args); // Check if result is cachedUsage Examples:
class DataAnalyzer {
constructor(dataset) {
this.dataset = dataset;
}
}
Object.defineProperties(DataAnalyzer.prototype, memoizeMethods({
analyzeData: d(function(analysisType) {
console.log(`Analyzing ${analysisType}...`);
return { type: analysisType, result: Math.random() };
}, { maxAge: 60000 })
}));
const analyzer = new DataAnalyzer([1, 2, 3]);
analyzer.analyzeData('mean'); // Computed
analyzer.analyzeData('median'); // Computed
// Check cache status
console.log(analyzer.analyzeData._has('mean')); // true
console.log(analyzer.analyzeData._has('mode')); // false
// Get cached value
const cachedMean = analyzer.analyzeData._get('mean');
console.log(cachedMean);
// Clear specific entry
analyzer.analyzeData.delete('mean');
// Clear all cached results for this instance
analyzer.analyzeData.clear();Use reference counting with method memoization for sophisticated memory management.
/**
* Reference counting methods for memoized methods
*/
const descriptors = memoizeMethods({
methodName: d(function(...args) {
// Method implementation
}, {
refCounter: true
})
});
// Additional methods available with refCounter
instance.methodName.deleteRef(...args); // Decrement reference
instance.methodName.getRefCount(...args); // Get reference countUsage Examples:
class ResourceManager {
constructor(id) {
this.id = id;
}
}
Object.defineProperties(ResourceManager.prototype, memoizeMethods({
createResource: d(function(resourceType) {
console.log(`Creating ${resourceType} resource...`);
return {
type: resourceType,
id: Math.random(),
cleanup: () => console.log(`Cleaning up ${resourceType}`)
};
}, {
refCounter: true,
dispose: (result) => {
if (result && result.cleanup) result.cleanup();
}
})
}));
const manager = new ResourceManager("mgr-1");
const resource1 = manager.createResource('database'); // refs: 1
const resource2 = manager.createResource('database'); // refs: 2 (cache hit)
console.log(manager.createResource.getRefCount('database')); // 2
manager.createResource.deleteRef('database'); // refs: 1
manager.createResource.deleteRef('database'); // refs: 0, cleanup calledCombine lazy initialization with method memoization for optimal performance.
class ExpensiveService {
constructor(config) {
this.config = config;
// Don't initialize expensive resources in constructor
}
}
Object.defineProperties(ExpensiveService.prototype, memoizeMethods({
// Lazy initialization - only run once per instance
initialize: d(function() {
console.log("Initializing expensive service...");
this.connection = createDatabaseConnection(this.config);
this.cache = new Map();
return this;
}),
// Use initialized resources
queryData: d(function(query) {
this.initialize(); // Ensure initialization (cached after first call)
console.log("Querying data...");
return this.connection.query(query);
}, {
async: true,
maxAge: 60000
})
}));
const service = new ExpensiveService({ host: 'localhost' });
// No expensive initialization yet
service.queryData('SELECT * FROM users'); // Initializes and queries
service.queryData('SELECT * FROM posts'); // Uses existing initializationHandle method memoization in inheritance hierarchies.
class BaseProcessor {
constructor(data) {
this.data = data;
}
}
// Base class memoized methods
Object.defineProperties(BaseProcessor.prototype, memoizeMethods({
baseProcess: d(function() {
console.log("Base processing...");
return this.data.length;
})
}));
class AdvancedProcessor extends BaseProcessor {
constructor(data, options) {
super(data);
this.options = options;
}
}
// Extended class additional memoized methods
Object.defineProperties(AdvancedProcessor.prototype, memoizeMethods({
advancedProcess: d(function(mode) {
console.log("Advanced processing...");
const baseResult = this.baseProcess(); // Uses memoized base method
return baseResult * (this.options.multiplier || 1);
}, { maxAge: 30000 })
}));
const advanced = new AdvancedProcessor([1, 2, 3], { multiplier: 2 });
advanced.baseProcess(); // Base method cached
advanced.advancedProcess('fast'); // Advanced method cached, reuses base resultMethod memoization creates separate caches per instance:
// Consider memory usage with many instances
const instances = [];
for (let i = 0; i < 1000; i++) {
const instance = new MyClass(data[i]);
instance.expensiveMethod(params); // Each instance gets its own cache
instances.push(instance);
}
// Use size limits for instances with large caches
Object.defineProperties(MyClass.prototype, memoizeMethods({
expensiveMethod: d(function(params) {
return heavyComputation(params);
}, {
max: 100, // Limit cache size per instance
maxAge: 300000 // Auto-expire entries
})
}));Method memoization is ideal when:
this context)Use regular memoization when:
this context