Comparing version 6.0.1 to 6.1.0
25
index.js
@@ -7,7 +7,11 @@ 'use strict'; | ||
const mem = (fn, { | ||
cacheKey = ([firstArgument]) => firstArgument, | ||
cache = new Map(), | ||
maxAge | ||
} = {}) => { | ||
const mem = (fn, options = {}) => { | ||
// Automatically use WeakMap unless the user provided their own cache | ||
const weakCache = options.cache || new WeakMap(); | ||
const { | ||
cacheKey = ([firstArgument]) => firstArgument, | ||
cache = new Map(), | ||
maxAge | ||
} = options; | ||
if (typeof maxAge === 'number') { | ||
@@ -20,4 +24,9 @@ mapAgeCleaner(cache); | ||
if (cache.has(key)) { | ||
return cache.get(key).data; | ||
// Prefer WeakMap if the key allows it | ||
const bestCache = key && (typeof key === 'object' || typeof key === 'function') ? | ||
weakCache : | ||
cache; | ||
if (bestCache.has(key)) { | ||
return bestCache.get(key).data; | ||
} | ||
@@ -27,3 +36,3 @@ | ||
cache.set(key, { | ||
bestCache.set(key, { | ||
data: cacheItem, | ||
@@ -30,0 +39,0 @@ maxAge: maxAge ? Date.now() + maxAge : Infinity |
{ | ||
"name": "mem", | ||
"version": "6.0.1", | ||
"version": "6.1.0", | ||
"description": "Memoize functions - An optimization used to speed up consecutive function calls by caching the result of calls with identical input", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
100
readme.md
@@ -7,3 +7,3 @@ # mem [![Build Status](https://travis-ci.org/sindresorhus/mem.svg?branch=master)](https://travis-ci.org/sindresorhus/mem) | ||
By default, **only the first argument is considered** and it only works with [primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). If you need to cache multiple arguments or cache `object`s *by value*, use the `cacheKey` option. | ||
By default, **only the first argument is considered** and it only works with [primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). If you need to cache multiple arguments or cache `object`s *by value*, have a look at alternative [caching strategies](#caching-strategy) below. | ||
@@ -85,3 +85,78 @@ | ||
### Caching strategy | ||
By default, only the first argument is compared via exact equality (`===`) to determine whether a call is identical. | ||
```js | ||
const power = mem((a, b) => Math.power(a, b)); | ||
power(2, 2); // => 4, stored in cache with the key 2 (number) | ||
power(2, 3); // => 4, retrieved from cache at key 2 (number), it's wrong | ||
``` | ||
You will have to use the `cache` and `cacheKey` options appropriate to your function. In this specific case, the following could work: | ||
```js | ||
const power = mem((a, b) => Math.power(a, b), { | ||
cacheKey: arguments_ => arguments_.join(',') | ||
}); | ||
power(2, 2); // => 4, stored in cache with the key '2,2' (both arguments as one string) | ||
power(2, 3); // => 8, stored in cache with the key '2,3' | ||
``` | ||
More advanced examples follow. | ||
#### Example: Options-like argument | ||
If your function accepts an object, it won't be memoized out of the box: | ||
```js | ||
const heavyMemoizedOperation = mem(heavyOperation); | ||
heavyMemoizedOperation({full: true}); // Stored in cache with the object as key | ||
heavyMemoizedOperation({full: true}); // Stored in cache with the object as key, again | ||
// The objects look the same but for JS they're two different objects | ||
``` | ||
You might want to serialize or hash them, for example using `JSON.stringify` or something like [serialize-javascript](https://github.com/yahoo/serialize-javascript), which can also serialize `RegExp`, `Date` and so on. | ||
```js | ||
const heavyMemoizedOperation = mem(heavyOperation, {cacheKey: JSON.stringify}); | ||
heavyMemoizedOperation({full: true}); // Stored in cache with the key '[{"full":true}]' (string) | ||
heavyMemoizedOperation({full: true}); // Retrieved from cache | ||
``` | ||
The same solution also works if it accepts multiple serializable objects: | ||
```js | ||
const heavyMemoizedOperation = mem(heavyOperation, {cacheKey: JSON.stringify}); | ||
heavyMemoizedOperation('hello', {full: true}); // Stored in cache with the key '["hello",{"full":true}]' (string) | ||
heavyMemoizedOperation('hello', {full: true}); // Retrieved from cache | ||
``` | ||
#### Example: Multiple non-serializable arguments | ||
If your function accepts multiple arguments that aren't supported by `JSON.stringify` (e.g. DOM elements and functions), you can instead extend the initial exact equality (`===`) to work on multiple arguments using [`many-keys-map`](https://github.com/fregante/many-keys-map): | ||
```js | ||
const ManyKeysMap = require('many-keys-map'); | ||
const addListener = (emitter, eventName, listener) => emitter.on(eventName, listener); | ||
const addOneListener = mem(addListener, { | ||
cacheKey: arguments_ => arguments_, // Use *all* the arguments as key | ||
cache: new ManyKeysMap() // Correctly handles all the arguments for exact equality | ||
}); | ||
addOneListener(header, 'click', console.log); // `addListener` is run, and it's cached with the `arguments` array as key | ||
addOneListener(header, 'click', console.log); // `addListener` is not run again | ||
addOneListener(mainContent, 'load', console.log); // `addListener` is run, and it's cached with the `arguments` array as key | ||
``` | ||
Better yet, if your function’s arguments are compatible with `WeakMap`, you should use [`deep-weak-map`](https://github.com/futpib/deep-weak-map) instead of `many-keys-map`. This will help avoid memory leaks. | ||
## API | ||
@@ -118,26 +193,13 @@ | ||
You can have it cache **all** the arguments by value with `JSON.stringify`, if they are compatible: | ||
Refer to the [caching strategies](#caching-strategy) section for more information. | ||
```js | ||
const mem = require('mem'); | ||
mem(function_, {cacheKey: JSON.stringify}); | ||
``` | ||
Or you can use a more full-featured serializer like [serialize-javascript](https://github.com/yahoo/serialize-javascript) to add support for `RegExp`, `Date` and so on. | ||
```js | ||
const mem = require('mem'); | ||
const serializeJavascript = require('serialize-javascript'); | ||
mem(function_, {cacheKey: serializeJavascript}); | ||
``` | ||
##### cache | ||
Type: `object`\ | ||
Default: `new Map()` | ||
Default: `new Map()`, but it also intelligently uses `new WeakMap()` whenevever possible | ||
Use a different cache storage. Must implement the following methods: `.has(key)`, `.get(key)`, `.set(key, value)`, `.delete(key)`, and optionally `.clear()`. You could for example use a `WeakMap` instead or [`quick-lru`](https://github.com/sindresorhus/quick-lru) for a LRU cache. | ||
Use a different cache storage. Must implement the following methods: `.has(key)`, `.get(key)`, `.set(key, value)`, `.delete(key)`, and optionally `.clear()`. You could for example use [`quick-lru`](https://github.com/sindresorhus/quick-lru) for a LRU cache. | ||
Refer to the [caching strategies](#caching-strategy) section for more information. | ||
### mem.clear(fn) | ||
@@ -144,0 +206,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
13708
133
256
0