类中Monkey Patch window.fetch触发Illegal invocation错误的求助
在类中Monkey Patch window.fetch的问题与解决
问题描述
尝试在类中通过Monkey Patch拦截window.fetch以获取请求和响应体,当前实现代码如下:
export class NetworkService implements Listener { originalFetch: typeof window.fetch constructor() { this.originalFetch = window.fetch } async listenFetch(): Promise<void> { if (typeof window.fetch !== 'function') return window.fetch = async (input: RequestInfo, init?: RequestInit) => { const response = await this.originalFetch(input, init); try { response .clone() .text() .then(body => console.log(body)) .catch(err => console.error(err)); } catch (e) { console.log('Error reading response: ' + e) } return response; }; } registerListeners(): void { this.listenFetch() } unregisterListeners(): void { if (typeof fetch !== 'function') return window.fetch = this.originalFetch } }
发起fetch请求时触发错误:
Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
尝试修改构造函数为以下形式时,又出现无限递归问题:
constructor( fetch: FetchFunction = (...args) => window.fetch(...args) ) { this.originalFetch = fetch; }
错误原因
- 非法调用的原因:
fetch是window对象的原生方法,它内部的执行依赖this指向window。直接将window.fetch赋值给this.originalFetch后,调用this.originalFetch(input, init)时,this指向的是NetworkService实例而非window,导致fetch内部上下文错误,触发"Illegal invocation"。 - 无限递归的原因:修改后的构造函数中,默认参数的箭头函数会调用当前的
window.fetch。当window.fetch已经被我们的patch函数替换后,这个箭头函数会递归调用patch后的fetch,进而无限循环。
解决方法
核心是确保原始fetch方法调用时的上下文为window,可以通过以下两种方式实现:
方式一:保存原始fetch时绑定window上下文
在构造函数中保存window.fetch时,用bind绑定window,确保后续调用时this指向正确:
export class NetworkService implements Listener { originalFetch: typeof window.fetch; constructor() { // 绑定window上下文,保证调用原始fetch时this指向window this.originalFetch = window.fetch.bind(window); } async listenFetch(): Promise<void> { if (typeof window.fetch !== 'function') return; window.fetch = async (input: RequestInfo, init?: RequestInit) => { const response = await this.originalFetch(input, init); try { // 使用await替代then链,代码更简洁 const body = await response.clone().text(); console.log(body); } catch (e) { console.error('Error reading response:', e); } return response; }; } registerListeners(): void { this.listenFetch(); } unregisterListeners(): void { if (typeof window.fetch !== 'function') return; // 恢复原始的fetch方法 window.fetch = this.originalFetch; } }
方式二:调用原始fetch时指定上下文
如果不想在保存时绑定,也可以在调用originalFetch时用call指定window作为上下文:
// 在listenFetch的patch函数中修改调用方式 const response = await this.originalFetch.call(window, input, init);
这两种方式都能解决非法调用的问题,同时因为直接保存了原始的window.fetch引用,不会出现无限递归的情况。
内容的提问来源于stack exchange,提问作者Bruno Francisco
相关产品推荐
相关产品推荐

