index.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. 'use strict';
  2. const mimicFn = require("mimic-fn");
  3. const mapAgeCleaner = require("map-age-cleaner");
  4. const decoratorInstanceMap = new WeakMap();
  5. const cacheStore = new WeakMap();
  6. /**
  7. [Memoize](https://en.wikipedia.org/wiki/Memoization) functions - An optimization used to speed up consecutive function calls by caching the result of calls with identical input.
  8. @param fn - Function to be memoized.
  9. @example
  10. ```
  11. import mem = require('mem');
  12. let i = 0;
  13. const counter = () => ++i;
  14. const memoized = mem(counter);
  15. memoized('foo');
  16. //=> 1
  17. // Cached as it's the same arguments
  18. memoized('foo');
  19. //=> 1
  20. // Not cached anymore as the arguments changed
  21. memoized('bar');
  22. //=> 2
  23. memoized('bar');
  24. //=> 2
  25. ```
  26. */
  27. const mem = (fn, { cacheKey, cache = new Map(), maxAge } = {}) => {
  28. if (typeof maxAge === 'number') {
  29. // TODO: Drop after https://github.com/SamVerschueren/map-age-cleaner/issues/5
  30. // @ts-expect-error
  31. mapAgeCleaner(cache);
  32. }
  33. const memoized = function (...arguments_) {
  34. const key = cacheKey ? cacheKey(arguments_) : arguments_[0];
  35. const cacheItem = cache.get(key);
  36. if (cacheItem) {
  37. return cacheItem.data;
  38. }
  39. const result = fn.apply(this, arguments_);
  40. cache.set(key, {
  41. data: result,
  42. maxAge: maxAge ? Date.now() + maxAge : Number.POSITIVE_INFINITY
  43. });
  44. return result;
  45. };
  46. mimicFn(memoized, fn, {
  47. ignoreNonConfigurable: true
  48. });
  49. cacheStore.set(memoized, cache);
  50. return memoized;
  51. };
  52. /**
  53. @returns A [decorator](https://github.com/tc39/proposal-decorators) to memoize class methods or static class methods.
  54. @example
  55. ```
  56. import mem = require('mem');
  57. class Example {
  58. index = 0
  59. @mem.decorator()
  60. counter() {
  61. return ++this.index;
  62. }
  63. }
  64. class ExampleWithOptions {
  65. index = 0
  66. @mem.decorator({maxAge: 1000})
  67. counter() {
  68. return ++this.index;
  69. }
  70. }
  71. ```
  72. */
  73. mem.decorator = (options = {}) => (target, propertyKey, descriptor) => {
  74. const input = target[propertyKey];
  75. if (typeof input !== 'function') {
  76. throw new TypeError('The decorated value must be a function');
  77. }
  78. delete descriptor.value;
  79. delete descriptor.writable;
  80. descriptor.get = function () {
  81. if (!decoratorInstanceMap.has(this)) {
  82. const value = mem(input, options);
  83. decoratorInstanceMap.set(this, value);
  84. return value;
  85. }
  86. return decoratorInstanceMap.get(this);
  87. };
  88. };
  89. /**
  90. Clear all cached data of a memoized function.
  91. @param fn - Memoized function.
  92. */
  93. mem.clear = (fn) => {
  94. const cache = cacheStore.get(fn);
  95. if (!cache) {
  96. throw new TypeError('Can\'t clear a function that was not memoized!');
  97. }
  98. if (typeof cache.clear !== 'function') {
  99. throw new TypeError('The cache Map can\'t be cleared!');
  100. }
  101. cache.clear();
  102. };
  103. module.exports = mem;