如何实现一个只能执行一次的函数?
实现一个 once 函数以确保给定函数只能被执行一次,并记忆其结果。该函数使用闭包来管理调用状态和结果。
实现一个 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 以复用结果;可选
}
};
}
工作原理与注意事项:
- 闭包机制:内部变量如
called
和result
在闭包中被记住,确保多次调用共享同一状态。 - 参数处理:使用展开运算符
...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
。