You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

类中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;
  }

错误原因

  1. 非法调用的原因:fetch是window对象的原生方法,它内部的执行依赖this指向window。直接将window.fetch赋值给this.originalFetch后,调用this.originalFetch(input, init)时,this指向的是NetworkService实例而非window,导致fetch内部上下文错误,触发"Illegal invocation"。
  2. 无限递归的原因:修改后的构造函数中,默认参数的箭头函数会调用当前的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 20:35:00