缓存其参数返回值的函数

21
我想编写一个名为once的函数,它接受一个回调函数作为输入,并返回一个函数。当调用返回的函数第一次时,它应该调用回调函数并返回该输出。如果多次调用该函数,则不会再次调用回调函数,而是仅返回第一次调用时的输出值。
以下是我尝试过的内容。但我没有得到我期望的结果。我需要理解这个概念。
function once(func) {
    let num; 
    function retFunc(x){
        num = func(x);
        return num;
    }
    return retFunc;
}

function addByTwo(input){
    return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4));  //should log 6
console.log(onceFunc(10));  //should log 6
console.log(onceFunc(9001));  //should log 6

1
“num” 已经是一个不错的第一步,但是如何记住它已经被称为第一次了呢?你 总是 会覆盖 “num = func(x)” - Bergi
顺便提一下,这被称为记忆化 - Barmar
5个回答

19
你应该只在第一次调用 retFunc 时将 num = func(x) 赋值给 num,并且此时 num 必须是 undefined。:

function once(func) {
  let num; 
  function retFunc(x){
    if (num === undefined) {
      num = func(x);
    }
    return num;
  }
  return retFunc;
}

function addByTwo(input){
  return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4));  //should log 6
console.log(onceFunc(10));  //should log 6
console.log(onceFunc(9001));  //should log 6
但这并不是一个保证通用解决方案 - 如果传递的函数(在你的示例中是addByTwo)被调用时产生undefined,那么=== undefined检查将不起作用。因此,最好设置一个标志或类似的东西,并在第一次调用回调时重新分配该标志:

但这并不是一个保证通用解决方案 - 如果传递的函数(在你的示例中是addByTwo)被调用时产生undefined,那么=== undefined检查将不起作用。因此,最好设置一个标志或类似的东西,并在第一次调用回调时重新分配该标志:

function once(func) {
  let num;
  let done = false;
  function retFunc(x){
    if (!done) {
      done = true;
      num = func(x);
    }
    return num;
  }
  return retFunc;
}

function returnsUndefinedOn1(input){
  return input === 1 ? undefined : input;
}

var onceFunc = once(returnsUndefinedOn1);

console.log(onceFunc(1));
console.log(onceFunc(10));
console.log(onceFunc(9001));


你好,我非常喜欢这个详细的解释。但是当我运行最后一段代码时,它输出:undefined。 - Amit Kumar
2
这是期望的行为 - 在输入为 1 的情况下,函数 returnsUndefinedOn1 根据其名称返回 undefined。尽管使用 其他 参数调用 onceFunc,但进一步调用也将返回 undefined - CertainPerformance
2
请使用 typeofundefined 进行比较。 - Pierre Arlaud
2
@PierreArlaud 在任何合理的代码中,我认为这是不必要的?许多全局对象可以被重新定义,这并不意味着它们不应该被使用,而是不要重新分配它们。 - CertainPerformance
1
@CertainPerformance,typeof也不会在变量未声明时引发异常。没有任何情况可以不使用typeof与undefined进行比较是更好的选择。我对此问题的看法是,使用typeof是一个好习惯,即使是“远程合理的代码”也不例外。 - Pierre Arlaud
3
在这种情况下,如果变量未声明,则获取异常是“可取的”,因为我们正在尝试测试我们知道刚刚声明的变量的值,而异常告诉我们,在变量名称中犯了一个拼写错误,代码已经出现故障。 - Ilmari Karonen

8
只有在 num 未定义时,才应调用该函数并赋值,否则每次都会覆盖它。

function once(func) {
  let num;

  function retFunc(x) {
    num = (num === undefined) ? func(x) : num
    return num;
  }
  return retFunc;
}

function addByTwo(input) {
  return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4)); //should log 6
console.log(onceFunc(10)); //should log 6
console.log(onceFunc(9001)); //should log 6

请注意,如果您传递的函数返回undefined,它将被调用多次。如果您需要处理这种情况,可以设置一个标志来指示缓存值是否有效。

哇,你在这里真的很有帮助,不是吗?难怪你有36.8k的声望。 - Yang K
@YangK 绝大部分时间都失眠! - Mark

3
您所缺少的是在第一次执行后删除原始函数。您应该按照以下方式修改代码:
function once(func) {
  let num; 
  function retFunc(x){
      if (func)
          num = func(x);
      func = null;
      return num;
 }
    return retFunc;
}

function addByTwo(input){
  return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4));  //should log 6
console.log(onceFunc(10));  //should log 6
console.log(onceFunc(9001));  //should log 6

这样,您在第一次使用后就删除了函数,只保留结果。

我想说的是,这种方式比依赖于结果更好,因为函数不需要总是返回一些东西。它可能返回未定义,但我们仍然希望只运行一次。 - tswistak

2

这是你的问题。

function retFunc(x){
        num = func(x);
        return num;
    }

你总是在调用func(x)。在调用func(x)之前,添加一个if条件来检查num是否为undefined


通过添加if条件来检查num是否未定义,我的代码现在可以工作了。 - Amit Kumar

1

不同的方法:覆盖对 func 的调用

无需标志,检查结果是否未定义或需要任何内容。

function once(func) {
    let num;

    let internalFn = function(x) {
        num = func(x);
        internalFn = function(x) {
            return num;
        }
        return num;
    }

    function retFunc(x){
        return internalFn(x);
   }
      return retFunc;
  }

  function addByTwo(input){
    return input + 2;
  }

  var onceFunc = once(addByTwo);

  console.log(onceFunc(4));  //should log 6
  console.log(onceFunc(10));  //should log 6
  console.log(onceFunc(9001));  //should log 6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接