UNPKG

3.06 kBJavaScriptView Raw
1import mimicFn from 'mimic-fn';
2import mapAgeCleaner from 'map-age-cleaner';
3const cacheStore = new WeakMap();
4/**
5[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.
6
7@param fn - Function to be memoized.
8
9@example
10```
11import mem from 'mem';
12
13let index = 0;
14const counter = () => ++index;
15const memoized = mem(counter);
16
17memoized('foo');
18//=> 1
19
20// Cached as it's the same argument
21memoized('foo');
22//=> 1
23
24// Not cached anymore as the arguments changed
25memoized('bar');
26//=> 2
27
28memoized('bar');
29//=> 2
30```
31*/
32export default function mem(fn, { cacheKey, cache = new Map(), maxAge, } = {}) {
33 if (typeof maxAge === 'number') {
34 mapAgeCleaner(cache);
35 }
36 const memoized = function (...arguments_) {
37 const key = cacheKey ? cacheKey(arguments_) : arguments_[0];
38 const cacheItem = cache.get(key);
39 if (cacheItem) {
40 return cacheItem.data; // eslint-disable-line @typescript-eslint/no-unsafe-return
41 }
42 const result = fn.apply(this, arguments_);
43 cache.set(key, {
44 data: result,
45 maxAge: maxAge ? Date.now() + maxAge : Number.POSITIVE_INFINITY,
46 });
47 return result; // eslint-disable-line @typescript-eslint/no-unsafe-return
48 };
49 mimicFn(memoized, fn, {
50 ignoreNonConfigurable: true,
51 });
52 cacheStore.set(memoized, cache);
53 return memoized;
54}
55/**
56@returns A [decorator](https://github.com/tc39/proposal-decorators) to memoize class methods or static class methods.
57
58@example
59```
60import {memDecorator} from 'mem';
61
62class Example {
63 index = 0
64
65 @memDecorator()
66 counter() {
67 return ++this.index;
68 }
69}
70
71class ExampleWithOptions {
72 index = 0
73
74 @memDecorator({maxAge: 1000})
75 counter() {
76 return ++this.index;
77 }
78}
79```
80*/
81export function memDecorator(options = {}) {
82 const instanceMap = new WeakMap();
83 return (target, propertyKey, descriptor) => {
84 const input = target[propertyKey]; // eslint-disable-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
85 if (typeof input !== 'function') {
86 throw new TypeError('The decorated value must be a function');
87 }
88 delete descriptor.value;
89 delete descriptor.writable;
90 descriptor.get = function () {
91 if (!instanceMap.has(this)) {
92 const value = mem(input, options);
93 instanceMap.set(this, value);
94 return value;
95 }
96 return instanceMap.get(this);
97 };
98 };
99}
100/**
101Clear all cached data of a memoized function.
102
103@param fn - Memoized function.
104*/
105export function memClear(fn) {
106 const cache = cacheStore.get(fn);
107 if (!cache) {
108 throw new TypeError('Can\'t clear a function that was not memoized!');
109 }
110 if (typeof cache.clear !== 'function') {
111 throw new TypeError('The cache Map can\'t be cleared!');
112 }
113 cache.clear();
114}