如何克隆XMLHttpRequest对象?两种尝试方案及报错解析
需求场景
需要克隆一个已完成配置(调用过open和setRequestHeader)的XMLHttpRequest对象,原代码如下:
let xhr = new XMLHttpRequest; xhr.open('GET', '/', true); xhr.setRequestHeader('Test', 'test'); let clone = ....? xhr.send(); clone.setRequestHeader('Test2', '....'); clone.send();
失败尝试与问题分析
尝试方案1
let xhr = new XMLHttpRequest; xhr.open('GET', '/', true); xhr.setRequestHeader('Test', 'test'); let clone = Object.assign(Object.create(Object.getPrototypeOf(xhr)), xhr); xhr.send(); clone.setRequestHeader('Test2', '....'); clone.send();
报错信息(翻译后):
Uncaught TypeError: 在未实现XMLHttpRequest接口的对象上调用了'setRequestHeader'方法。
问题原因:
XMLHttpRequest是浏览器提供的原生宿主对象,其内部接口和状态无法通过Object.create+Object.assign的方式模拟。这种方法创建的对象仅继承了XHR的原型结构,并非真正的XMLHttpRequest实例,因此无法调用其原生方法。
尝试方案2
let xhr = new XMLHttpRequest; xhr.open('GET', '/', true); xhr.setRequestHeader('Test', 'test'); let clone = new XMLHttpRequest; Object.assign(clone, xhr); xhr.send(); clone.setRequestHeader('Test2', '....'); clone.send();
报错信息(翻译后):
Uncaught DOMException: XMLHttpRequest.setRequestHeader: XMLHttpRequest的状态必须是OPENED。
问题原因:Object.assign仅能复制XHR对象的可枚举属性,但XMLHttpRequest的内部状态(如是否执行过open)属于不可枚举的私有属性,无法通过此方式复制。新创建的clone实例未执行open方法,状态未进入OPENED阶段,因此调用setRequestHeader会触发报错。
正确解决方案
XMLHttpRequest实例的内部状态无法直接克隆,可靠的方式是抽离请求配置逻辑,用统一的配置对象初始化多个XHR实例:
// 抽离请求配置 const requestConfig = { method: 'GET', url: '/', async: true, headers: { 'Test': 'test' } }; // 初始化原XHR并发送 const xhr = new XMLHttpRequest; xhr.open(requestConfig.method, requestConfig.url, requestConfig.async); for (const [key, value] of Object.entries(requestConfig.headers)) { xhr.setRequestHeader(key, value); } xhr.send(); // 克隆配置并初始化新XHR const clone = new XMLHttpRequest; clone.open(requestConfig.method, requestConfig.url, requestConfig.async); // 复制原请求头 for (const [key, value] of Object.entries(requestConfig.headers)) { clone.setRequestHeader(key, value); } // 添加新请求头 clone.setRequestHeader('Test2', 'new-value'); clone.send();
补充说明
由于XMLHttpRequest API未提供直接获取已设置请求头的方法,无法从已配置的XHR实例中反向提取完整配置。因此提前封装配置对象,是实现“配置复用/克隆”的最优方案。
内容的提问来源于stack exchange,提问作者Ivan

