@exadel/esl
Version:
Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components
76 lines (75 loc) • 3.22 kB
JavaScript
import { defaultArgsHashFn, memoizeFn } from '../misc/memoize';
import { isPrototype, getPropertyDescriptor } from '../misc/object';
/**
* Memoization decorator helper.
* @see memoizeFn Original memoizeFn function decorator.
*/
export function memoize(hashFn = defaultArgsHashFn) {
return function (target, prop, descriptor) {
if (!descriptor || typeof (descriptor.value || descriptor.get) !== 'function') {
throw new TypeError('Only get accessors or class methods can be decorated via @memoize');
}
if (isPrototype(target)) {
// Object members
(typeof descriptor.get === 'function') && (descriptor.get = memoizeGetter(descriptor.get, prop));
(typeof descriptor.value === 'function') && (descriptor.value = memoizeMethod(descriptor.value, prop, hashFn));
}
else {
// Static members
(typeof descriptor.get === 'function') && (descriptor.get = memoizeFn(descriptor.get));
(typeof descriptor.value === 'function') && (descriptor.value = memoizeFn(descriptor.value, hashFn));
}
};
}
// Lock storage to prevent cache logic for some key
const locks = new WeakMap();
const defineOwnKeySafe = (obj, prop, value) => {
locks.set(obj, prop); // IE try to get key with the prototype instance call, so we lock it
Object.defineProperty(obj, prop, { value, writable: true, configurable: true });
locks.delete(obj); // Free property key
};
/** Cache getter result as an object own property */
function memoizeGetter(originalMethod, prop) {
return function () {
if (locks.get(this) === prop)
return originalMethod;
const value = originalMethod.call(this);
defineOwnKeySafe(this, prop, value);
return value;
};
}
/** Cache method memo function in the current context on call */
function memoizeMethod(originalMethod, prop, hashFn) {
return function (...args) {
if (locks.get(this) === prop)
return originalMethod;
const memo = memoizeFn(originalMethod, hashFn);
defineOwnKeySafe(this, prop, memo);
return memo.apply(this, args);
};
}
function clearMemo(target, property) {
if (Array.isArray(property))
return property.forEach((prop) => memoize.clear(target, prop));
const desc = getPropertyDescriptor(target, property);
if (!desc)
return;
if (typeof desc.get === 'function' && typeof desc.get.clear === 'function')
return desc.get.clear();
if (typeof desc.value === 'function' && typeof desc.value.clear === 'function')
return desc.value.clear();
if (Object.hasOwnProperty.call(target, property))
delete target[property];
}
memoize.clear = clearMemo;
function hasMemo(target, property, ...params) {
const desc = getPropertyDescriptor(target, property);
if (!desc)
return false;
if (typeof desc.get === 'function' && typeof desc.get.has === 'function')
return desc.get.has(...params);
if (typeof desc.value === 'function' && typeof desc.value.has === 'function')
return desc.value.has(...params);
return Object.hasOwnProperty.call(target, property);
}
memoize.has = hasMemo;