标准答案

  1. once 需要在闭包里保存是否已经执行过。
  2. 第一次调用时执行原函数,并保存返回值。
  3. 后续调用直接返回第一次的结果。
  4. 调用原函数时要保留 this 和参数。
  5. 如果原函数可能抛错,要明确失败是否也算已经执行过。

题目解析

once 常用于初始化、事件绑定、埋点上报、弹窗展示等只允许发生一次的操作。它控制的是执行次数,不是防抖或节流。

保留 this 和参数很重要。被包装的函数可能是对象方法,也可能依赖第一次调用传入的参数。

异常策略要说清楚。很多实现会在函数成功返回后才标记为已执行;也有实现会把抛错也视为一次执行,这取决于业务语义。

代码示例

下面的版本在原函数成功返回后缓存结果。

JavaScript
function once(fn) {
  let called = false
  let result

  return function onceWrapper(...args) {
    if (called) {
      return result
    }

    result = fn.apply(this, args)
    called = true
    return result
  }
}

常见误区

  • 只用全局变量记录状态,导致多个 once 包装函数互相影响。
  • 执行原函数时没有保留 this 和参数。
  • 没有明确原函数抛错时是否允许下一次重试。

作者信息