为何XMLHttpRequest属性仅能通过console.log()打印?如何创建同类对象?
为啥XMLHttpRequest的属性只能被console.log打印,Object系列方法却拿不到?
先看看你执行的代码和对应的输出:
执行代码
var obj = new XMLHttpRequest(); console.log("Object.keys():",Object.keys(obj)); console.log("Object.getOwnPropertyNames():",Object.getOwnPropertyNames(obj)) console.log("Object.entries():",Object.entries(obj)) console.log("JSON.stringify():",JSON.stringify(obj)) console.log("console.log:"); console.log(obj)
输出结果
Object.keys(): []
Object.getOwnPropertyNames(): []
Object.entries(): []
JSON.stringify(): {}
console.log:
XMLHttpRequest {onreadystatechange: null, readyState: 0, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
核心原因:XMLHttpRequest是「宿主对象」,和普通JS对象不一样
你遇到的这个差异,本质是**宿主对象(Host Object)**和JavaScript原生对象的实现逻辑不同:
- 像
XMLHttpRequest这种对象是浏览器(JS运行环境)提供的,不是JS引擎原生的普通对象。它的属性大多定义在原型链上,而且被设置为enumerable: false(不可枚举)。 Object.keys()、Object.getOwnPropertyNames()这些方法,要么只找对象自身的可枚举属性,要么只找自身的属性(不管枚举),完全不会去遍历原型链上的内容,所以拿不到那些属性。- 而浏览器的
console.log是专门做过优化的工具,它会主动遍历对象的整条原型链,甚至能读取一些浏览器内部维护的状态属性,所以能把这些“隐藏”的属性显示出来。 - 至于
JSON.stringify(),它只序列化对象自身的可枚举属性,还会忽略函数、Symbol类型的属性,自然输出空对象{}。
如何在JS中创建类似的对象?
我们可以结合原型链和不可枚举属性来模拟这个效果,虽然没法完全复刻宿主对象的内部黑魔法,但能实现「Object方法拿不到,console.log能显示」的核心需求:
基础模拟版本
// 定义构造函数 function MySimulatedHostObj() { // 这里不放自身属性,把属性都挂到原型上,模拟XHR的结构 } // 在原型上定义一批不可枚举的属性 Object.defineProperties(MySimulatedHostObj.prototype, { readyState: { value: 0, enumerable: false, // 关键:设置为不可枚举 writable: true }, timeout: { value: 0, enumerable: false, writable: true }, onreadystatechange: { value: null, enumerable: false, writable: true } }); // 创建实例 const myObj = new MySimulatedHostObj(); // 测试效果 console.log(Object.keys(myObj)); // [] console.log(Object.getOwnPropertyNames(myObj)); // [] console.log(JSON.stringify(myObj)); // {} console.log(myObj); // 会显示原型上的readyState、timeout等属性(不同浏览器显示细节略有不同)
进阶:添加动态访问器属性
如果想要模拟XHR那种动态计算的属性(比如status),还可以用访问器属性:
// 在原型上添加动态getter Object.defineProperty(MySimulatedHostObj.prototype, 'status', { get() { // 模拟根据readyState返回不同状态码 return this.readyState === 4 ? 200 : 0; }, enumerable: false // 同样设置为不可枚举 }); // 修改readyState后,status会动态变化 myObj.readyState = 4; console.log(myObj.status); // 200
需要注意的是,真正的宿主对象比如XHR还有很多浏览器内部的特殊逻辑(比如upload是另一个宿主对象),这些细节很难完全模拟,但上面的方法已经能满足你要的核心效果了。
内容的提问来源于stack exchange,提问作者Marinos An
相关产品推荐
相关产品推荐

