函数重写配合闭包是轻量、即时的api拦截增强手段:通过闭包捕获原函数并动态覆盖目标方法,实现“预处理→执行原逻辑→后增强”闭环,兼具安全性与可控性。

函数重写(Monkey Patching)配合闭包,是一种轻量、即时、不依赖框架的 API 拦截与增强手段。它不修改源码,也不引入代理层,而是直接替换目标函数引用,同时用闭包保存原始逻辑和上下文状态,实现“拦截前预处理 → 执行原逻辑 → 拦截后增强”的闭环。
核心思路:用闭包封装原函数 + 动态覆盖全局/原型方法
关键在于两点:一是确保原函数在重写前被安全捕获并封闭在闭包中;二是新函数体里可自由插入自定义逻辑,且仍能调用原行为。闭包天然维持对原函数的引用,避免污染全局或意外丢失。
- 不要直接在重写函数里写
originalFn(...)—— 若原函数后续又被其他 patch 覆盖,就会失效;必须在闭包内“快照”一次 - 优先 patch 实例方法或构造器原型上的方法(如
XMLHttpRequest.prototype.send),而非全局函数(如fetch),更安全可控 - 为避免重复 patch,可用私有标记(如
__patched_by_mylib)判断是否已处理
实战:拦截 fetch 并自动注入 token 与错误统一处理
以下代码在页面加载早期执行,仅 patch 一次,后续所有 fetch 调用都会经过增强:
if (!window.fetch.__patched_by_auth) {
const originalFetch = window.fetch;
window.fetch = function(input, init = {}) {
// 拦截前:注入 token
const token = localStorage.getItem('auth_token');
if (token && !init.headers) {
init.headers = { 'Authorization': `Bearer ${token}` };
} else if (token && init.headers instanceof Headers) {
init.headers.set('Authorization', `Bearer ${token}`);
}
// 执行原 fetch,并返回 Promise 链
return originalFetch(input, init)
.then(response => {
// 拦截后:检查 401 并触发登出(只做一次)
if (response.status === 401) {
const logoutOnce = (function() {
let called = false;
return function() {
if (!called) {
called = true;
localStorage.removeItem('auth_token');
location.href = '/login';
}
};
})();
logoutOnce();
}
return response;
})
.catch(err => {
// 全局网络异常兜底
console.warn('[Fetch Interceptor] Network error:', err);
throw err;
});
};
window.fetch.__patched_by_auth = true;
}
这里闭包体现在两处:一是 originalFetch 被封闭在外部作用域,不受后续重写影响;二是 logoutOnce 是立即执行函数返回的闭包,保证登出逻辑只触发一次 —— 这正是“函数重写 + 闭包控制”协同的价值。
进阶技巧:支持多级拦截与条件启用
若需叠加多个增强(如日志、超时、重试),可把重写逻辑抽象为可组合的闭包工厂:
const withAuth = (fn) => (input, init) => {
const token = localStorage.getItem('auth_token');
if (token) init.headers = { ...init.headers, Authorization: `Bearer ${token}` };
return fn(input, init);
};
const withTimeout = (ms, fn) => (input, init) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
const initWithSignal = { ...init, signal: controller.signal };
return fn(input, initWithSignal)
.finally(() => clearTimeout(timeoutId));
};
// 组合使用(顺序重要)
window.fetch = withTimeout(8000, withAuth(window.fetch));
这种写法把每个增强逻辑封装为高阶函数,通过闭包捕获各自配置(如 ms、token key),再链式重写 fetch,清晰、可测、易维护。
注意事项与边界
Monkey Patching 不是万能方案,需清醒认知其局限:
- 无法拦截已编译的 WebAssembly 或原生浏览器内部调用(如
console.log的底层实现) - 某些现代 API(如
Request构造函数)创建的对象可能绕过 patch,需同步 patch 构造器或使用Proxy增强实例 - 若第三方库自身也 patch 同一方法(如某些 SDK 重写
XMLHttpRequest),要注意执行顺序与兼容性 - 测试环境需还原原始函数,建议封装
unpatch()方法或使用 Jest 的jest.mock()替代
本质上,这是用最小侵入代价换取最大灵活性的工程选择——适合快速验证、灰度发布、调试辅助或遗留系统增强,不复杂但容易忽略闭包的作用边界。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










