A blazing fast memoization library that is tiny but feature-rich.
npm install micro-memoizeA blazing fast memoization library that is tiny but feature-rich.
- micro-memoize
- Table of contents
- Importing
- Usage
- Types
- Composition
- Options
- async
- expires
- forceUpdate
- isKeyEqual
- isKeyItemEqual
- deep
- shallow
- maxArgs
- maxSize
- serialize
- statsName
- transformKey
- Additional properties
- memoized.cache
- memoized.cache.clear
- memoized.cache.delete(args)
- memoized.cache.get(args)
- memoized.cache.has(args)
- memoized.cache.on
- add
- delete
- hit
- update
- memoized.cache.set(args, value)
- memoized.cache.snapshot
- memoized.fn
- memoized.isMemoized
- memoized.options
- Statistics
- clearStats
- [getStats([statsName])](#getstatsstatsname)
- isCollectingStats
- startCollectingStats
- stopCollectingStats
- Benchmarks
- Single primitive parameter
- Single array parameter
- Single object parameter
- Multiple primitive parameters
- Multiple array parameters
- Multiple object parameters
- Support
- Development
ESM:
``ts`
import { memoize } from 'micro-memoize';
CommonJS:
`ts`
const { memoize } = require('micro-memoize');
`ts
const toObject = (one: string, two: string) => ({ one, two });
const memoized = memoize(toObject);
console.log(memoized('one', 'two'));
console.log(memoized('one', 'two')); // pulled from cache
// Starting in 4.0.0, you can compose memoized functions if you want to have multiple types of memoized
// versions based on different options.
const withUpToFive = memoize(memoized, { maxSize: 5 }); // { maxSize: 5 }
const withAsync = memoize(withUpToFive, { async: true }); // { async: true, maxSize: 5 }
const withCustomEquals = memoize(withAsync, { isEqual: deepEqual }); // { async: true, maxSize: 5, isEqual: deepEqual }
`
NOTE: The original function is the function used in the composition, the composition only applies to the options. In
the example above, upToFive does not call simple, it calls fn.
Identifies the value returned from the method as a Promise, which will result in one of two possible scenarios:
- If the promise is resolved, it will fire the hit and update cache events.
- If the promise is rejected, it will trigger auto-removal from cache as fire the delete cache event.
`ts
const fn = async (one: string, two: string) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error(JSON.stringify({ one, two })));
}, 500);
});
};
const memoized = memoize(fn, { isPromise: true });
memoized('one', 'two');
console.log(memoized.cache.snapshot.keys); // [['one', 'two']]
console.log(memoized.cache.snapshot.values); // [Promise]
setTimeout(() => {
console.log(memoized.cache.snapshot.keys); // []
console.log(memoized.cache.snapshot.values); // []
}, 1000);
`
NOTE: If you don't want rejections to auto-remove the entry from cache, set async to false (or simply do not set
it), but be aware this will also remove the cache listeners that fire on successful resolution.
The amount of time in milliseconds that you want a computed value to be stored in cache for this method.
`ts
const fn = (item: Record
const MAX_AGE = 1000 60 5; // five minutes;
const expiringMemoized = memoize(fn, { expires: MAX_AGE });
`
This can also be dynamic based on the entry, if a method is passed:
`ts
const fn = (item: Record
const expiringMemoized = memoize(fn, {
expires: (key, value) => shouldExpire(key, value),
});
`
You can also pass a custom configuration to handle conditional expiration.
`ts`
const conditionalExpiringMemoized = memoize(fn, {
expires: {
after: MAX_AGE,
shouldPersist: (item) => !item.expires,
shouldRemove: (item) => item.updatedAt < new Date('2025-01-01').valueOf(),
update: true,
},
});
TIP: A common usage of this is in tandem with async for AJAX calls, and in that scenario the expected behavior isexpires
usually to have the countdown begin upon resolution of the promise. If this is your intended use case, youupdate
should also apply the configuration option.
Updates the cache forcibly for a given key when the predicate returns true. This is mainly useful if the function being
memoized has time-based side-effects.
`ts
const fn = (item: string) => item;
let lastUpdate = Date.now();
const memoized = memoize(fn, {
forceUpdate([item]: [string]) {
const now = Date.now();
const last = lastUpdated;
lastUpdate = now;
// its been more than 5 minutes since last update
return last + 300000 < now;
},
});
memoized('one');
memoized('one'); // pulled from cache
await Promise.resolve(() => setTimeout(resolve, MAX_AGE));
memoized('one'); // re-calls method and updates cache
`
Custom method to compare equality of keys, determining whether to pull from cache or not, by comparing the entire key.
`ts
type Arg = {
one: string;
two: null | string;
};
const fn = ({ one, two }: Arg) => [one, two];
const isFooEqualAndHasBar = (cacheKey: [Arg], key: [Arg]) =>
cacheKey[0].one === key[0].one && cacheKey[1].hasOwnProperty('two') && key[1].hasOwnProperty('two');
const memoized = memoize(fn, { isKeyEqual: isFooEqualAndHasBar });
memoized({ one: 'two' }, { two: null });
memoized({ one: 'two' }, { two: 'three' }); // pulls from cache
`
_defaults to Object.is_
Custom method to compare equality of keys, determining whether to pull from cache or not, by comparing each argument in
order. There are simple string options available for deep / shallow equality comparisons, or you can pass your own
function for custom comparison.
`ts
type Arg = {
one: {
nested: string;
};
two: string;
};
const fn = ({ one, two }: Arg) => [one, two];
const deepMemoized = memoize(fn, { isKeyItemEqual: 'deep' });
deepMemoized({ one: { nested: 'one' }, two: 'two' });
deepMemoized({ one: { nested: 'one' }, two: 'two' }); // pulls from cache
const shallowMemoized = memoize(fn, { isKeyItemEqual: 'shallow' });
shallowMemoized({ one: 'one', two: 'two' });
shallowMemoized({ one: 'one', two: 'two' }); // pulls from cache
const customMemoized = memoize(fn, {
isKeyItemEqual: (cacheKeyArg: Arg, keyArg: Arg) =>
Object.keys(cacheKeyArg).length === 1 && Object.keys(keyArg).length === 1
}
);
customMemoized({ one: 'two' };
customMemoized({ two: 'three' }); // pulls from cache
`
#### deep
Performs a deep equality comparison of each key item using the deepEqual method from fast-equals.
#### shallow
Performs a shallow equality comparison using the shallowEqual method from fast-equals.
The maximum number of arguments (starting from the first) used in creating the key for the cache.
`ts
const fn = (item1: string, item2: string, item3: string) => item1 + item2 + item3;
const memoized = memoize(fn, { maxArgs: 2 });
memoize('one', 'two', 'three');
memoize('one', 'two', 'four'); // pulls from cache, as the first two args are the same
`
If maxArgs is combined with either serialize or transformKey, the following order is used:
1. transform by transformKey (if applicable)maxArgs
1. limit by serializer
1. serialize by (if applicable)
_defaults to 1_
The number of values to store in cache, based on a
Least Recently Used basis.
This operates the same as maxSize on memoize.
`ts
const manyPossibleArgs = (one: string, two: string) => [one, two];
const memoized = memoize(manyPossibleArgs, { maxSize: 3 });
memoized.cache.on('delete', (event) => console.log('Deleted from cache: ', event.key));
console.log(memoized('one', 'two')); // ['one', 'two']
console.log(memoized('two', 'three')); // ['two', 'three']
console.log(memoized('three', 'four')); // ['three', 'four']
console.log(memoized('one', 'two')); // pulled from cache
console.log(memoized('two', 'three')); // pulled from cache
console.log(memoized('three', 'four')); // pulled from cache
console.log(memoized('four', 'five')); // ['four', 'five'], Deleted from cache: ['one', 'two']
`
Serializes the parameters passed into a string and uses this as the key for cache comparison. If a method is passed
instead of a boolean, it is used as a custom serializer.
`ts
const fn = (mutableObject: { one: Record
const serializedMemoized = memoize(fn, { serialize: true });
const customSerializedMemoized = memoize(fn, {
serialize: (args) => [JSON.stringify(args[0])],
});
`
If serialize is combined with either maxArgs or transformKey, the following order is used:
1. transform by transformKey (if applicable)maxArgs
1. limit by (if applicable)
1. serialize
NOTE: This is much slower than the default key storage, and usually the same requirements can be meet with
isKeyItemEqual: 'deep', so use at your discretion.
Name to use as unique identifier for the function when collecting statistics. Applying a statsName will also activate
stats collection for that method.
`ts
startCollectingStats();
const fn = (item: string) => ({ item });
const memoized = memoize(fn, { statsName: 'my fancy identity' });
memoized('foo');
memoized('foo');
memoized('foo');
console.log(getStats('my fancy identity')); // { calls: 3, hits: 2, name: "my fancy identity", usage: "66.666666%" }
`
Method that allows you transform the key that is used for caching, if you want to use something other than the arguments
passed.
`ts
const ignoreFunctionArgs = (one: string, two: () => {}) => [one, two];
const memoized = memoize(ignoreFunctionArgs, {
transformKey: (args) => [JSON.stringify(args[0])],
});
console.log(memoized('one', () => {})); // ['one', () => {}]
console.log(memoized('one', () => {})); // pulled from cache, ['one', () => {}]
`
If your transformed keys require something other than
SameValueZero equality, you can combine
transformKey with isKeyEqual for completely custom key creation and comparison.
`ts
const ignoreFunctionArg = (one: string, two: () => void) => [one, two];
const memoized = memoize(ignoreFunctionArg, {
isMatchingKey: (key1, key2) => key1[0] === key2[0],
// Cache based on the serialized first parameter
transformKey: (args) => [JSON.stringify(args[0])],
});
console.log(memoized('one', () => {})); // ['one', () => {}]
console.log(memoized('one', () => {})); // pulled from cache, ['one', () => {}]
`
If transformKey is combined with either maxArgs or serialize, the following order is used:
1. transform by transformKeymaxArgs
1. limit by (if applicable)
1. serialize (if applicable)
The cache object that is used internally. This is a highly-optimized structure, but has several methods for manual
cache manipulation.Direct cache manipulation
The cache is an optimized linked list internally, so working with the cache directly is advised against. However, there
are several exposed ways to introspect or manually manipulate the cache based on common use-cases.
#### memoized.cache.clear()
This will clear all values in the cache, resetting it to an empty state.
`ts
const memoized = memoize((item: string) => item);
memoized.cache.clear();
`
#### memoized.cache.delete(args)
This will remove the key based on the provided args from cache. args should be an Array of values, meant to
reflect the arguments passed to the method.
`ts
const memoized = memoize((item: { one: string }) => item);
const arg = { one: 'one' };
memoized(arg);
memoized.cache.delete([arg]);
// will re-execute, as it is no longer in cache
memoized(arg);
`
NOTE: This will only remove keys that exist in the cache, and will do nothing if the key does not exist.
#### memoized.cache.get(args)
Returns the value in cache if the key based on args matches, else returns undefined. args should be an Array of
values, meant to reflect the arguments passed to the method.
`ts
const memoized = memoize((one: string, two: string) => [one, two);
memoized('one', 'two');
console.log(memoized.cache.get(['one', 'two'])); // ["one","two"]
console.log(memoized.cache.get(['two', 'three'])); // undefined
`
#### memoized.cache.has(args)
This will return true if a cache entry exists for the key based on the args passed, else will return false. argsArray
should be an of values, meant to reflect the arguments passed to the method.
`ts
const memoized = memoize((one: string, two: string) => [one, two]);
memoized('one', 'two');
console.log(memoized.cache.has(['one', 'two'])); // true
console.log(memoized.cache.has(['two', 'three'])); // false
`
#### memoized.cache.on(name, listener)
Event listeners are available for different cache events, so that you can monitor cache changes over time.
##### add
Fires when an item has been added to cache. Receives the event:
`ts`
interface OnAddEvent
cache: Cache
key: Key;
reason: string | undefined;
type: 'add';
value: ReturnType
}
Example:
`ts
const fn = (one: string, two: string) => [one, two];
const memoized = memoize(fn, { maxSize: 2 });
memoized.cache.on('add', (event) => {
console.log(cache has been added to: ${JSON.stringify(event.key)});
});
memoized('foo', 'bar'); // cache has been added to: ["foo","bar"]
memoized('foo', 'bar');
memoized('foo', 'bar');
memoized('bar', 'foo'); // cache has been added to: ["bar","foo"]
memoized('bar', 'foo');
memoized('bar', 'foo');
memoized('foo', 'bar');
memoized('foo', 'bar');
memoized('foo', 'bar');
`
##### delete
Fires when an item has been removed from cache. Receives the event:
`ts`
interface OnDeleteEvent
cache: Cache
key: Key;
reason: string | undefined;
type: 'delete';
value: ReturnType
}
Example:
`ts
const fn = (one: string, two: string) => [one, two];
const memoized = memoize(fn);
memoized.cache.on('delete', (event) => {
console.log(cache entry was deleted (${event.reason}): ${JSON.stringify(event.key)});
});
memoized('foo', 'bar');
memoized('foo', 'bar');
memoized('foo', 'bar');
memoized('bar', 'foo'); // cache entry was deleted (evicted): ["foo","bar"]
memoized('bar', 'foo');
memoized('bar', 'foo');
memoized('foo', 'bar'); // cache entry was deleted (evicted): ["bar","foo"]
memoized('foo', 'bar');
memoized('foo', 'bar');
`
##### hit
Fires when an item has been found in cache. Receives the event:
`ts`
interface OnHitEvent
cache: Cache
key: Key;
reason: string | undefined;
type: 'hit';
value: ReturnType
}
Example:
`ts
const fn = (one: string, two: string) => [one, two];
const memoized = memoize(fn, { maxSize: 2 });
memoized.cache.on('hit', (event) => {
console.log(cache entry found: ${JSON.stringify(event.key)});
});
memoized('foo', 'bar');
memoized('foo', 'bar'); // cache entry was found: ["foo","bar"]
memoized('foo', 'bar'); // cache entry was found: ["foo","bar"]
memoized('bar', 'foo');
memoized('bar', 'foo'); // cache entry was found: ["bar","foo"]
memoized('bar', 'foo'); // cache entry was found: ["bar","foo"]
memoized('foo', 'bar'); // cache entry was found: ["foo","bar"]
memoized('foo', 'bar'); // cache entry was found: ["foo","bar"]
memoized('foo', 'bar'); // cache entry was found: ["foo","bar"]
`
##### update
Fires when cache was reordered based on finding an older entry in cache and making it the most recent. Receives the
event:
`ts`
interface OnUpdateEvent
cache: Cache
key: Key;
reason: string | undefined;
type: 'update';
value: ReturnType
}
Example:
`ts
const fn = (one: string, two: string) => [one, two];
const memoized = memoize(fn, { maxSize: 2 });
memoized.cache.on('update', (event) => {
console.log(cache has updated: ${JSON.stringify(event.key)});
});
memoized('foo', 'bar');
memoized('foo', 'bar');
memoized('bar', 'foo');
memoized('foo', 'bar'); // cache has updated: ["foo","bar"]
memoized('bar', 'foo'); // cache has updated: ["bar","foo"]
memoized('foo', 'bar'); // cache has updated: ["foo","bar"]
memoized('foo', 'bar');
`
#### memoized.cache.set(args, value)
This will manually add the value at the key based on args in cache if the key does not already exist; if the keyargs
exists, it will update the value. should be an Array of values, meant to reflect the arguments passed to the
method.
`ts
// single parameter is straightforward
const memoized = memoize((item: string) => item: string);
memoized.add(['one'], 'two');
// pulls from cache
memoized('one');
`
#### memoized.cache.snapshot
The cache is mutated internally for performance reasons, so logging out the cache at a specific step in the workflowcache.snapshot
may not give you the information you need. As such, to help with debugging you can request the , which
provides a well-formed snapshot of the cache:
`ts`
type CacheSnapshot = {
entries: Array<[Key, ReturnType
keys: Key[];
size: number;
values: Array
};
The original function passed to be memoized.
Hard-coded to true when the function is memoized. This is useful for introspection, to identify if a method has been
memoized or not.
The options passed when creating the memoized method.
As-of version 5, you can collect statistics of memoize to determine if your cached methods are effective. To activate
stats collection for a given memoized method, you must provide a statsName.
`ts
import { getStats, memoize, startCollectingStats } from 'memoize';
startCollectingStats();
const fn = (one: string, two: string) => [one, two];
const foo = memoize((one: string, two: string) => [one, two], {
statsName: 'foo',
});
const bar = memoize((one: string, two: string) => ${one} ${two}, {
statsName: 'bar',
});
// this will have no stats collected
const baz = memoize((one: string, two: string) => ({ one, two }));
foo('one', 'two');
bar('one', 'two');
foo('one', 'two');
baz('one', 'two');
console.log(getStats('foo'));
/*
{
"calls": 2,
"hits": 1,
"name": "foo",
"usage": "50.000000%"
}
*/
console.log(getStats());
/*
{
"calls": 3,
"hits": 1,
"profiles: {
foo: {
"calls": 2,
"hits": 1,
"name": "foo",
"usage": "50.000000%"
},
"bar": {
"calls": 1,
"hits": 0,
"name: "bar",
"usage": "0.000000%"
}
},
"usage": "33.333333%"
}
*/
`
NOTE: It is recommended not to activate this in production, as it has a small (but unnecessary) performance impact.
Cear statistics on memoized functions.
`ts`
clearStats(); // clears all stats
clearStats('stats-name'); // clears stats only for 'stats-name'
Get the statistics for a specific function, or globally.
`ts
startCollectingStats();
const fn = (one: string, two: string) => [one, two];
const memoized = memoize(fn);
const otherFn = (one: string[]) => one.slice(0, 1);
const otherMemoized = memoize(otherFn, { statsName: 'otherMemoized' });
memoized('one', 'two');
memoized('one', 'two');
otherMemoized(['three']);
getStats('otherMemoized');
/*
{
"calls": 1,
"hits": 0,
"name": "otherMemoized",
"usage": "0.000000%"
}
*/
getStats();
/*
{
"calls": 3,
"hits": 1,
"profiles": {
"otherMemoized": {
"calls": 1,
"hits": 0,
"name": "otherMemoized",
"usage": "0.000000%"
}
},
"usage": "33.3333%"
}
*/
`
Are statistics being collected on memoization usage.
`ts`
startCollectingStats();
isCollectingStats(); // true
stopCollectingStats();
isCollectingStats(); // false
Start collecting statistics on memoized functions with defined statsName options.
`ts`
startCollectingStats();
s;
Stop collecting statistics on memoized functions with defined statsName options.
`ts`
stopCollectingStats();
All values provided are the number of operations per second (ops/sec) calculated by the
Benchmark suite. Note that underscore, lodash, and ramda do not supportmicro-memoize
mulitple-parameter memoization (which is where really shines), so they are not included in those
benchmarks.
Benchmarks was performed on an i9 16-core Linux laptop with 64GB of memory using NodeJS version 24.11.0. The default
configuration of each library was tested with a fibonacci calculation based on the following parameters:
- Single primitive = 35[35]
- Single array = { number: 35 }
- Single object = 35, true
- Multiple primitives = [35], [true]
- Multiple arrays = { number: 35 }, { isComplete: true }
- Multiple objects =
NOTE: Not all libraries tested support multiple parameters out of the box, but support the ability to pass a custom
resolver. Because these often need to resolve to a string value,JSON.stringify
a common suggestion is to just the arguments, so that
is what is used when needed.
`bash
┌───────────────┬─────────────────┐
│ Name │ Ops / sec │
├───────────────┼─────────────────┤
│ micro-memoize │ 19223652.896122 │
├───────────────┼─────────────────┤
│ lru-memoize │ 18832637.003931 │
├───────────────┼─────────────────┤
│ fast-memoize │ 18656534.751361 │
├───────────────┼─────────────────┤
│ mem │ 18002171.563837 │
├───────────────┼─────────────────┤
│ lodash │ 16593919.372016 │
├───────────────┼─────────────────┤
│ ramda │ 15005783.713612 │
├───────────────┼─────────────────┤
│ addy osmani │ 14595725.136778 │
├───────────────┼─────────────────┤
│ underscore │ 14394123.608684 │
├───────────────┼─────────────────┤
│ memoizee │ 14143422.526051 │
├───────────────┼─────────────────┤
│ memoizerific │ 9098305.800331 │
└───────────────┴─────────────────┘
Fastest was "micro-memoize".
`
`bash`
┌───────────────┬─────────────────┐
│ Name │ Ops / sec │
├───────────────┼─────────────────┤
│ micro-memoize │ 17778297.542508 │
├───────────────┼─────────────────┤
│ lodash │ 16538329.518316 │
├───────────────┼─────────────────┤
│ lru-memoize │ 15486317.927651 │
├───────────────┼─────────────────┤
│ memoizee │ 12094740.371154 │
├───────────────┼─────────────────┤
│ memoizerific │ 8539559.392909 │
├───────────────┼─────────────────┤
│ mem │ 5084143.134978 │
├───────────────┼─────────────────┤
│ ramda │ 4779283.1256 │
├───────────────┼─────────────────┤
│ underscore │ 4536871.295139 │
├───────────────┼─────────────────┤
│ addy osmani │ 3930939.284558 │
├───────────────┼─────────────────┤
│ fast-memoize │ 2280509.909947 │
└───────────────┴─────────────────┘
Fastest was "micro-memoize".
`bash`
┌───────────────┬─────────────────┐
│ Name │ Ops / sec │
├───────────────┼─────────────────┤
│ micro-memoize │ 17660028.460248 │
├───────────────┼─────────────────┤
│ lodash │ 16156245.220626 │
├───────────────┼─────────────────┤
│ lru-memoize │ 15870878.729231 │
├───────────────┼─────────────────┤
│ memoizee │ 12282231.140112 │
├───────────────┼─────────────────┤
│ memoizerific │ 8673522.519546 │
├───────────────┼─────────────────┤
│ mem │ 3990242.123108 │
├───────────────┼─────────────────┤
│ ramda │ 3740951.604329 │
├───────────────┼─────────────────┤
│ underscore │ 3610838.023906 │
├───────────────┼─────────────────┤
│ addy osmani │ 3091088.143196 │
├───────────────┼─────────────────┤
│ fast-memoize │ 2037255.509585 │
└───────────────┴─────────────────┘
Fastest was "micro-memoize".
`bash`
┌───────────────┬─────────────────┐
│ Name │ Ops / sec │
├───────────────┼─────────────────┤
│ micro-memoize │ 15890715.277889 │
├───────────────┼─────────────────┤
│ lru-memoize │ 14795822.850331 │
├───────────────┼─────────────────┤
│ memoizee │ 8878241.434757 │
├───────────────┼─────────────────┤
│ memoizerific │ 6746446.702835 │
├───────────────┼─────────────────┤
│ addy osmani │ 5088726.104365 │
├───────────────┼─────────────────┤
│ mem │ 4919428.914271 │
├───────────────┼─────────────────┤
│ ramda │ 3931584.638274 │
├───────────────┼─────────────────┤
│ underscore │ 3787236.410261 │
├───────────────┼─────────────────┤
│ lodash │ 3521399.9522 │
├───────────────┼─────────────────┤
│ fast-memoize │ 1921167.148268 │
└───────────────┴─────────────────┘
Fastest was "micro-memoize".
`bash`
┌───────────────┬─────────────────┐
│ Name │ Ops / sec │
├───────────────┼─────────────────┤
│ micro-memoize │ 16477581.429235 │
├───────────────┼─────────────────┤
│ lru-memoize │ 15973386.171668 │
├───────────────┼─────────────────┤
│ memoizee │ 8916765.084082 │
├───────────────┼─────────────────┤
│ memoizerific │ 7394462.762389 │
├───────────────┼─────────────────┤
│ mem │ 4230286.398649 │
├───────────────┼─────────────────┤
│ ramda │ 3424812.638986 │
├───────────────┼─────────────────┤
│ underscore │ 3271019.504488 │
├───────────────┼─────────────────┤
│ lodash │ 3058024.57579 │
├───────────────┼─────────────────┤
│ addy osmani │ 2556872.10781 │
├───────────────┼─────────────────┤
│ fast-memoize │ 1664942.513511 │
└───────────────┴─────────────────┘
Fastest was "micro-memoize".
`bash`
┌───────────────┬─────────────────┐
│ Name │ Ops / sec │
├───────────────┼─────────────────┤
│ micro-memoize │ 16816183.60597 │
├───────────────┼─────────────────┤
│ lru-memoize │ 15847710.978627 │
├───────────────┼─────────────────┤
│ memoizee │ 8963861.306564 │
├───────────────┼─────────────────┤
│ memoizerific │ 7279084.235795 │
├───────────────┼─────────────────┤
│ mem │ 2891756.11455 │
├───────────────┼─────────────────┤
│ ramda │ 2562787.384413 │
├───────────────┼─────────────────┤
│ underscore │ 2508806.688495 │
├───────────────┼─────────────────┤
│ lodash │ 2413141.440839 │
├───────────────┼─────────────────┤
│ addy osmani │ 1882996.512818 │
├───────────────┼─────────────────┤
│ fast-memoize │ 1431840.065495 │
└───────────────┴─────────────────┘
Fastest was "micro-memoize".
- Chrome 47+
- Firefox 43+
- Edge 14s+
- Opera 34+
- Safari 10+
- Node 6+
Standard stuff, clone the repo and npm install dependencies. The npm scripts available:
- benchmark => run benchmarks against well-known alternative packagesbuild
- => run rollup to build the dist filesbuild:cjs
- => run rollup to build the dist files specific to CJS requiresbuild:esm
- => run rollup to build the dist files specific to ESM importsbuild:min
- => run rollup to build the dist files specific to pre-minified filesbuild:umd
- => run rollup to build the dist files specific to legacy environments, such as Node 10clean
- : remove dist folderclean:cjs
- : remove dist/cjs folderclean:esm
- : remove dist/esm folderclean:min
- : remove dist/min folderclean:umd
- : remove dist/umd folderdev
- => run webpack dev server to run example app (playground!)dist
- => runs build and build-minifiedlint
- => run ESLint against all files in the src folderrelease
- => runs the release process, which publishes the latest version of the packagerelease:beta
- => runs the beta release process, which publishes the next beta version of the packagerelease:scripts
- => run the precursor tasks as part of the release processtest
- => run unit teststest:coverage
- => run test but with coveragetest:watch
- => run test but with persistent watchertypecheck
- => run tsc` to verify types are valid