UNPKG

@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
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;