如何更正此缓存功能

时间:2019-01-01 17:11:19

标签: javascript ecmascript-6

我正在尝试创建一个缓存的函数,但是即使将缓存的值存储在变量中之后,它始终会触发未缓存结果的情况。

在我的代码中,我将结果存储在变量cachedValue中。 只有在参数('arg')中传递了新值时,才应调用我应该缓存的函数('foo')

function cacheFunction(foo) {
  const cachedValue = [];
  return (arg) => {
    if (cachedValue[arg]) {
      return cachedValue[arg];
    }
    cachedValue[arg] = foo(arg);
    return cachedValue[arg];
  };
}

export { cacheFunction };

测试代码

import { cacheFunction } from './cacheFunction';

describe('cacheFunction', () => {
  it('should cache function results and not rerun the original callback if the same arguments are presented', () => {
    const foo = jest.fn();
    const myCachedFunction = cacheFunction(foo);
    myCachedFunction(true);
    myCachedFunction(true);
    myCachedFunction(true);
    myCachedFunction(true);
    myCachedFunction(true);
    myCachedFunction(10);
    myCachedFunction(10);
    myCachedFunction(10);
    myCachedFunction(10);
    myCachedFunction(10);
    expect(foo).toHaveBeenCalledTimes(2);
// It is called 10 times
  });
});

2 个答案:

答案 0 :(得分:3)

您的模拟功能是错误的。 jest.fn(不带参数)仅提供返回undefined的模拟函数。您需要一个根据接收到的参数返回某些内容的对象。也许:

const foo = jest.fn(x => x);

然后,as I said in a comment这是不可靠的:

if (cachedValue[arg]) {

如果foo返回0怎么办?还是""?还是null?还是undefined? (实际上,您的模拟函数确实可以。)

相反,使用

if (Object.prototype.hasOwnProperty.call(cachedValue, arg)) {

但是正如@NinaScholz所说:只有在arg可以合理地强制转换为字符串而不会丢失信息/导致错误匹配的情况下,它才有效。地图将是更好的选择,并且在arg是对象的情况下使用WeakMap会更好。

对于它的价值:

function cacheFunction(foo) {
  const cachedValuesByPrimitive = new Map();
  const cachedValuesByObject = new WeakMap();
  return (arg) => {
    const cache = typeof arg === "object" && arg !== null
        ? cachedValuesByObject
        : cachedValuesByPrimitive;
    if (cache.has(arg)) {
      return cache.get(arg);
    }
    const result = foo(arg);
    cache.set(arg, result);
    return result;
  };
}

export { cacheFunction };

实时示例:

function cacheFunction(foo) {
  const cachedValuesByPrimitive = new Map();
  const cachedValuesByObject = new WeakMap();
  return (arg) => {
    const cache = typeof arg === "object" && arg !== null
        ? cachedValuesByObject
        : cachedValuesByPrimitive;
    if (cache.has(arg)) {
      return cache.get(arg);
    }
    const result = foo(arg);
    cache.set(arg, result);
    return result;
  };
}

function foo(x) {
  console.log("foo called for", x);
  return x;
}

const cachingFoo = cacheFunction(foo);

cachingFoo(true);
cachingFoo(true);
cachingFoo(true);
cachingFoo(true);
cachingFoo(10);
cachingFoo(10);
cachingFoo(10);
cachingFoo(10);
const obj1 = {a: 1};
cachingFoo(obj1);
cachingFoo(obj1);
const obj2 = {a: 2};
cachingFoo(obj2);
cachingFoo(obj2);
cachingFoo(obj2);

答案 1 :(得分:0)

只需替换if语句检查:

if (cachedValue[arg]) {
   return cachedValue[arg];
}

成为:

if (cachedValue.hasOwnProperty(arg)) {
   return cachedValue[arg];
}

因为每个元素都存储为undefined,所以第一个检查if (cachedValue[arg])返回undefined,在Javascript中将其视为false

我认为如果为此目的使用对象而不是数组也更好,所以替换

const cachedValue = [];

成为:

const cachedValue = {};