如何实现一个只能执行一次的函数?

实现一个 once 函数以确保给定函数只能被执行一次,并记忆其结果。该函数使用闭包来管理调用状态和结果。

JavaScript 中等 闭包 高阶函数

实现一个 once 函数涉及创建一个高阶函数,该函数接受一个函数作为参数,并返回一个新的函数。返回的新函数只能执行原函数一次,后续调用直接返回第一次的结果或 undefined。这利用了 JavaScript 闭包的特性来记忆执行状态。以下是典型的实现方式和原理:

function once(fn) {
    let called = false;  // 标记是否执行过一次
    let result;  // 用于存储第一次执行结果,便于后续返回相同值

    return function(...args) {
        if (!called) {  // 第一次执行
            called = true;
            result = fn.apply(this, args);  // 处理 this 上下文和参数
            return result;
        } else {  // 已调用过
            return undefined;  // 或 return result 以复用结果;可选
        }
    };
}

工作原理与注意事项:

  • 闭包机制:内部变量如 calledresult 在闭包中被记住,确保多次调用共享同一状态。
  • 参数处理:使用展开运算符 ...args 支持任意参数数量;apply(this, args) 确保了调用者语境正确。
  • 首次执行保护called = true; 在执行后立即设置,以防止函数执行异常时被重复调用。
  • 异常处理建议:添加函数类型检查如 if (typeof fn !== 'function') { throw new Error('参数必须为函数'); }

示例使用与测试:

const add = (a, b) => a + b;
const runOnce = once(add);

console.log(runOnce(1, 2));  // 输出 3(第一次执行)
console.log(runOnce(3, 4));  // 输出 undefined(后续调用不执行 add)
console.log(runOnce(3, 4));  // 输出 undefined

性能与最佳实践:

  • 轻量级闭包使用:实现简单,仅添加两个内部变量,不影响内存性能。
  • 函数引用清理:添加 fn = null;fn = undefined; 在首次执行后释放对原函数的引用以优化垃圾回收。
  • 变体考虑:如果需求不保留结果,直接返回 undefined;若需返回第一次结果,设置 return result