惰性函数本质是“只测一次,后续直走”,通过闭包缓存环境判定结果(如useAddEventListener),首次调用执行检测并封装结果,后续调用直接使用缓存值,避免重复判断,安全、可控、易调试且工程友好。

惰性函数本质是“只测一次,后续直走”,特别适合高频调用中需反复判断运行环境(如浏览器特性、Node 版本、设备能力)的场景。闭包是实现它的自然载体——它能安全保存判定结果,并让后续调用绕过重复检测。
核心思路:用闭包缓存判定结果,首次调用完成初始化
不重写函数本身,而是借助闭包把判断逻辑和结果封装在私有作用域里,返回一个始终高效的函数。这种方式比“函数自覆盖”更可控、更易调试,也避免了污染函数对象属性的风险。
- 首次调用时执行完整检测(如检查
addEventListener是否存在),得出结论并存入闭包变量 - 后续所有调用直接使用缓存值,跳过 if/else 分支和 API 检查
- 闭包确保该缓存变量不会被外部篡改,也不污染全局或函数自身属性
典型应用:跨浏览器事件绑定优化
下面是一个生产可用的惰性事件绑定函数,用闭包实现,无副作用、可复用:
const addEvent = (function() {
// 一次性判定,闭包内私有
const useAddEventListener = typeof window !== 'undefined' &&
window.addEventListener !== undefined;
return function(element, type, handler) {
if (useAddEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, handler);
} else {
element['on' + type] = handler;
}
};
})();
无论 addEvent 被调用 100 次还是 10 万次,环境检测只发生一次,且判定结果(useAddEventListener)被安全封闭在闭包中。
进阶技巧:支持运行时动态 fallback 或重试
若环境可能变化(如测试中模拟不同 UA、PWA 离线切换),可在闭包中加入轻量控制开关:
- 导出一个
resetDetection()方法,用于手动刷新判定(仅调试或特殊场景) - 将判定逻辑抽成独立函数,在闭包中缓存其返回值,而非布尔字面量,便于未来扩展多态策略
- 对高敏感场景(如 WebAssembly 支持检测),可加一层 try/catch 封装,防止判定过程报错中断主流程
对比提醒:为什么不用“函数自覆盖”?
虽然自覆盖写法(addEvent = function() {...})也能达成惰性效果,但它有明显隐患:
- 函数引用可能被提前缓存(如
const fn = addEvent),导致后续调用仍走旧逻辑 - 破坏函数的可预测性,不利于 tree-shaking 和静态分析
- 在严格模式或某些打包工具下可能触发警告或异常
- 闭包方案天然支持模块化导出、单元测试 mock,工程友好度更高

















