Repository: sindresorhus/mem
Pull request #35 (2019-05-09 06:38:54)

Cache function reference when it's a single argument
Since the very first version, mem has a _"cache if it's not a function AND not an object"_, which I don't understand because functions are can't be serialized via `JSON.stringify`

In short (unless I'm drunk 🤔) I think `mem` should _always_ use the ONE argument as key, unless it's a serializable object.

sindresorhus: I think the reason I didn't support functions is that the user can set their own `cache`, and it might be something that needs to serialize the value.
bfred-it: > and it might be something that needs to serialize the value.

In that case they can also set `cacheKey`, since it goes hand in hand with `cache`'s key-ing mechanism.
sindresorhus: > In that case they can also set cacheKey, since it goes hand in hand with cache's key-ing mechanism.

I agree. Can you add a note about that in the readme? Just so we're clear about how it works. It might not be obvious to everyone that we cache the function reference too.
bfred-it: > It might not be obvious to everyone that we cache the function reference too.

Why would it be otherwise? I think at most we could mention that _objects and multiple arguments_ are cached by value.

Patch:
diff --git a/index.d.ts b/index.d.ts
index 7866255..86acff4 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -20,7 +20,7 @@ declare namespace mem {
 		readonly maxAge?: number;
 
 		/**
-		Determines the cache key for storing the result based on the function arguments. By default, if there's only one argument and it's a [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive), it's used directly as a key, otherwise it's all the function arguments JSON stringified as an array.
+		Determines the cache key for storing the result based on the function arguments. By default, if there's only one argument and it's a [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive), it's used directly as a key (if it's a `function`, its reference will be used as key), otherwise it's all the function arguments JSON stringified as an array.
 
 		You could for example change it to only cache on the first argument `x => JSON.stringify(x)`.
 		*/
diff --git a/index.js b/index.js
index 51faf01..5669a0a 100644
--- a/index.js
+++ b/index.js
@@ -12,11 +12,9 @@ const defaultCacheKey = (...arguments_) => {
 
 	if (arguments_.length === 1) {
 		const [firstArgument] = arguments_;
-		if (
-			firstArgument === null ||
-			firstArgument === undefined ||
-			(typeof firstArgument !== 'function' && typeof firstArgument !== 'object')
-		) {
+		const isObject = typeof firstArgument === 'object' && firstArgument !== null;
+		const isPrimitive = !isObject;
+		if (isPrimitive) {
 			return firstArgument;
 		}
 	}
diff --git a/readme.md b/readme.md
index add4222..65f10bf 100644
--- a/readme.md
+++ b/readme.md
@@ -101,7 +101,7 @@ Milliseconds until the cache expires.
 
 Type: `Function`
 
-Determines the cache key for storing the result based on the function arguments. By default, if there's only one argument and it's a [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive), it's used directly as a key, otherwise it's all the function arguments JSON stringified as an array.
+Determines the cache key for storing the result based on the function arguments. By default, if there's only one argument and it's a [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive), it's used directly as a key (if it's a `function`, its reference will be used as key), otherwise it's all the function arguments JSON stringified as an array.
 
 You could for example change it to only cache on the first argument `x => JSON.stringify(x)`.
 


Test patch:
diff --git a/test.js b/test.js
index f48e4b7..d1b692c 100644
--- a/test.js
+++ b/test.js
@@ -15,6 +15,20 @@ test('memoize', t => {
 	t.is(memoized('foo', 'bar'), 2);
 	t.is(memoized('foo', 'bar'), 2);
 	t.is(memoized('foo', 'bar'), 2);
+	t.is(memoized(1), 3);
+	t.is(memoized(1), 3);
+	t.is(memoized(null), 4);
+	t.is(memoized(null), 4);
+	t.is(memoized(undefined), 5);
+	t.is(memoized(undefined), 5);
+	t.is(memoized(fixture), 6);
+	t.is(memoized(fixture), 6);
+	t.is(memoized(true), 7);
+	t.is(memoized(true), 7);
+
+	// Ensure that functions are stored by reference and not by "value" (e.g. their `.toString()` representation)
+	t.is(memoized(() => i++), 8);
+	t.is(memoized(() => i++), 9);
 });
 
 test('memoize with multiple non-primitive arguments', t => {