惰性载入函数是“只做一次,之后全走捷径”,首次调用时执行环境检测并重写自身为精简版本,后续调用直接运行最优路径,避免重复判断。

惰性载入函数不是“等会儿再做”,而是“只做一次,之后全走捷径”。它把环境检测、兼容性判断、资源初始化这类高成本操作,推迟到函数第一次被调用时才执行,并立即重写自身为一个精简、专用的版本。后续调用完全跳过所有分支和检测逻辑,直接运行最优路径。
为什么环境嗅探适合用惰性载入
浏览器能力检测(如 addEventListener 是否存在)、运行时特征判断(如是否支持 getComputedStyle)、模块可用性检查(如 fetch 或 XMLHttpRequest)——这些结果在单页生命周期内是稳定不变的。反复判断同一条件,纯属冗余计算。
- 普通写法每次调用都走 if/else,哪怕页面里调用 100 次,也重复判断 100 次
- 预加载嗅探(IIFE 方式)在脚本加载时就执行,哪怕函数压根没被调用,也白耗资源
- 惰性载入只在真正需要时才检测,且仅一次,检测完立刻切换成“免检通道”
两种主流实现方式及写法要点
核心在于:函数首次执行时,根据环境决定新函数体,并将自身重新赋值;然后立即调用新函数。关键细节决定是否健壮:
- 函数重写方式:直接给函数名重新赋值,注意必须在重写后立刻调用,否则会报“Maximum call stack size exceeded”(递归爆栈)
- 闭包封装方式:用 IIFE 返回一个内部已缓存判断结果的函数,避免污染全局函数引用,更适合模块化或多人协作场景
- 判断顺序建议从最现代、最常用的能力开始(如优先查
addEventListener),提高首调命中率 - 务必覆盖兜底分支(如 IE8 及更早),否则在不支持的环境中会静默失败
典型实战示例:跨浏览器事件绑定
以下是一个生产可用的惰性载入 addEvent 函数:
if (el.addEventListener) {
addEvent = function(el, type, handler) {
el.addEventListener(type, handler, false);
};
} else if (el.attachEvent) {
addEvent = function(el, type, handler) {
el.attachEvent('on' + type, handler);
};
} else {
addEvent = function(el, type, handler) {
if (el['on' + type]) {
el['on' + type] = handler;
} else {
el.setAttribute('on' + type, handler.toString());
}
};
}
return addEvent(el, type, handler);
}
第一次调用后,addEvent 就变成一个纯执行函数,不再含任何 if 判断。
进阶提醒:该注意什么
惰性载入虽高效,但容易埋下隐患:
- 函数重写后,原函数引用失效——若其他变量曾保存旧版函数(如
const oldAdd = addEvent),后续调用仍走原始逻辑,导致行为不一致 - 调试时查看函数体,看到的是重写后的版本,原始判断逻辑已不可见,需靠注释或文档补全上下文
- 单元测试需覆盖所有分支路径,因为每个分支都会生成不同版本的函数,不能只测“默认路径”
- 在模块打包(如 Webpack)环境下,若函数被 tree-shaking 误删,或被压缩工具混淆重命名,可能导致重写失败


















