Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
/**
* Caches the return value of get accessors and methods.
*
* Notes:
* - Doesn't really make sense to put this on a method with parameters.
* - Creates an obscure non-enumerable property on the instance to store the memoized value.
* - Could use a WeakMap, but this way has support in old environments.
*/
export function Memoize(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<any>) {
if (descriptor.value != null) {
descriptor.value = getNewFunction(descriptor.value);
}
else if (descriptor.get != null) {
descriptor.get = getNewFunction(descriptor.get);
}
else {
throw "Only put a Memoize decorator on a method or get accessor.";
}
}
let counter = 0;
function getNewFunction(originalFunction: () => void) {
const identifier = ++counter;
return function (this: any, ...args: any[]) {
const propName = `__memoized_value_${identifier}`;
let returnedValue: any;
if (this.hasOwnProperty(propName)) {
returnedValue = this[propName];
}
else {
returnedValue = originalFunction.apply(this, args);
Object.defineProperty(this, propName, {
configurable: false,
enumerable: false,
writable: false,
value: returnedValue
});
}
return returnedValue;
};
}
// ------------ ES6 VERSION ----------
export function Memoize(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<any>) {
if (descriptor.value != null)
descriptor.value = getNewFunction(descriptor.value);
else if (descriptor.get != null)
descriptor.get = getNewFunction(descriptor.get);
else
throw new Error("Only put a Memoize decorator on a method or get accessor.");
}
const weakMap = new WeakMap<object, Map<string, unknown>>();
let counter = 0;
function getNewFunction(originalFunction: (...args: any[]) => void) {
const identifier = counter++;
function decorator(this: any, ...args: any[]) {
let propertyValues = weakMap.get(this);
if (propertyValues == null) {
propertyValues = new Map<string, unknown>();
weakMap.set(this, propertyValues);
}
let propName = `__memoized_value_${identifier}`;
if (arguments.length > 0)
propName += "_" + JSON.stringify(args);
let returnedValue: any;
if (propertyValues.has(propName))
returnedValue = propertyValues.get(propName);
else {
returnedValue = originalFunction.apply(this, args);
propertyValues.set(propName, returnedValue);
}
return returnedValue;
}
return decorator;
}
@smnbbrv
Copy link

smnbbrv commented May 24, 2019

Here is a WeakMap based version of this decorator, that respects single argument (by a reference for non-primitives)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment