You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用闭包与装饰器实现仅可调用一次的JavaScript函数?

实现仅能调用一次的函数(闭包+装饰器方式)

问题描述

我希望用闭包和装饰器实现一个仅能调用一次的函数,原以为理解相关原理,但实际编写时遇到了问题:

初始代码

export function once(callback) {
  return (...args) => {

    function once(fun){
      let called = false;
    
      return () => {
        if(!called){
          const result = fun(...arguments);
          called = true;
          return result;
        }
      }
    }
    // 必须仅调用一次`callback`!
    // 必须缓存其返回值。
    return callback(...args);  // 多次调用时无效果
  };
}

我尝试将最后一行修改为:

...
return once(callback(...args));
  };
}

这不应该能实现函数仅调用一次的效果吗?


问题分析与正确实现

你的代码存在两个核心问题:

  • 内部嵌套的once函数未生效:外层once返回的箭头函数每次被调用时,都会重新定义内部的once,最后直接执行callback(...args),等于每次调用都会触发原回调,完全没用到闭包的状态控制逻辑。
  • 修改后的代码逻辑错误:once(callback(...args))是先执行了callback并把结果传给内部once,而不是传入回调函数本身。这会导致原回调在传入时就已经执行,后续返回的函数根本无法控制调用次数。

正确实现

核心思路是用外层函数的作用域保存called状态和回调结果,让返回的闭包函数共享这个状态:

export function once(callback) {
  let called = false;
  let result;
  
  return (...args) => {
    if (!called) {
      result = callback(...args);
      called = true;
    }
    return result;
  };
}

逻辑说明

  • called和result定义在外层once函数中,每次调用once(callback)都会创建独立的状态变量,不同的“单次调用函数”之间不会互相影响。
  • 返回的箭头函数每次被调用时,先检查called:未调用则执行原回调、缓存结果并标记已调用;已调用则直接返回缓存的结果。
  • 最终实现原回调仅执行一次,后续调用均返回第一次的结果。

测试示例

const logMessage = () => {
  console.log('这只打印一次');
  return '第一次的返回值';
};

const logOnce = once(logMessage);
logOnce(); // 打印"这只打印一次",返回"第一次的返回值"
logOnce(); // 无打印,返回"第一次的返回值"
logOnce(); // 同上,无打印,返回缓存值

内容的提问来源于stack exchange,提问作者MyNameIsArjen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 05:09:10