闭包实现函数只执行一次的核心是用私有变量记录执行状态,首次调用执行原函数并缓存结果,后续调用直接返回缓存值;需用apply保证this绑定,兼顾参数传递与幂等性。

用闭包实现函数只执行一次,核心思路是:**利用闭包保存一个状态变量(如布尔值或计数器),在函数内部判断该状态,若已执行过则直接返回,不再重复执行逻辑**。
基础写法:返回新函数并封装执行状态
最常用的方式是写一个高阶函数,接收目标函数,返回一个“只执行一次”的包装函数:
- 闭包内部定义 私有变量
hasBeenCalled = false - 返回的函数每次调用时先检查
hasBeenCalled,为true就跳过执行 - 首次调用时执行原函数,并将
hasBeenCalled设为true
示例:
function once(fn) {
let hasBeenCalled = false;
let result;
return function(...args) {
if (hasBeenCalled) return result;
result = fn.apply(this, args);
hasBeenCalled = true;
return result;
};
}
<p>// 使用
const sayHello = once(() => console.log('Hello!'));
sayHello(); // 输出 "Hello!"
sayHello(); // 无输出</p>支持返回值和 this 绑定
上面的写法已兼顾了 this 和参数传递(用 apply + ...args),且缓存首次调用的返回值。这是生产环境推荐的健壮版本 —— 不仅限制执行次数,还保证多次调用返回相同结果,符合“幂等”语义。
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
注意:
- 若原函数依赖 this(如对象方法),必须用 fn.apply(this, args) 而非 fn(...args),否则 this 会丢失
- 缓存 result 可避免重复计算,尤其适合耗时或有副作用的操作(如 API 请求、DOM 初始化)
更简洁的现代写法(可选)
如果不需要兼容老环境,可用箭头函数 + 闭包变量简化(但无法正确绑定 this):
const once = (fn) => {
let called = false, res;
return (...args) => {
if (called) return res;
res = fn(...args);
called = true;
return res;
};
};
⚠️ 此写法中 this 指向调用时的上下文(通常是全局或 undefined),不适用于需要原始 this 的场景(如类方法)。稳妥起见,优先用带 apply 的版本。
实际应用场景举例
这类模式常见于:
- 初始化逻辑:页面加载时只绑定一次事件监听器或初始化第三方 SDK
- 资源懒加载:首次访问某个模块时才创建实例,后续复用
- 防重复提交:表单提交按钮点击后禁用,同时确保请求只发一次(配合 Promise 更佳)
例如防重复提交可组合使用:
const submitOnce = once(async (data) => {
await fetch('/api/submit', { method: 'POST', body: JSON.stringify(data) });
});

















