Why properties of an XMLHttpRequest object are only printable through console.log()?

This has to do with the WhatWG console.log specification:

How the implementation prints args is up to the implementation, but implementations should separate the objects by a space or something similar, as that has become a developer expectation.

The spec leaves the output format very vague and it's up to the implementation to decide what to print.


You can use the new data type called Symbol to create the keys for an JavaScript object.

Link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

I have attached an sample code and you can see the output in console.

let nameSymbol = Symbol('name');
function Person (name) {
	this[nameSymbol] = name;
}

Person.prototype.toString = function toPersonString() {
	var symArr = Object.getOwnPropertySymbols(this);
	for (let sym of symArr) {
		this[sym.description] = this[sym];
		delete this[sym];
	}
	return this;
}

const dummyObject = new Person('random value');

console.log("Object.keys():",Object.keys(dummyObject)); // []
console.log("Object.getOwnPropertyNames():",Object.getOwnPropertyNames(dummyObject)) // []
console.log("Object.entries():",Object.entries(dummyObject)) // []
console.log("JSON.stringify():",JSON.stringify(dummyObject)) // {}

console.log("console.log:", dummyObject.toString()); //  {name: "random value"}


All the properties on an XMLHttpRequest instance are inherited properties, what i mean by that is that they don't exist on the instance itself but rather on its prototype chain.

This explains why

const x = new XMLHttpRequest()
// all these methods consider only instance properties (owned properties) and thats why they return empty results
console.log(Object.keys(x)) // []
console.log(Object.getOwnPropertyNames(x)) // []
console.log(Object.entries(x)) // []
console.log(JSON.stringify(x)) // {}


// however if you try this you will get the results you want

console.log(Object.keys(x.__proto__)) // will not return symbol keys
console.log(Object.getOwnPropertyDescriptors(x.__proto__)) // will return descriptiors of every property whether its symbol or not

// you can see these results also by logging the xmlRequest, and looking at its __proto__ property in the console

// i will be calling the XMLHttpRequest instance for short as xmlRequest

You can create such an object simply like this

class A {}
A.prototype.someProperty = 10
const a = new A()
// again if you try these you will get results like the XMLHttpRequest instance
console.log(Object.keys(a)) // []
console.log(Object.getOwnPropertyNames(a)) // []
console.log(Object.entries(a)) // []
console.log(JSON.stringify(a)) // {}

however when you try to console.log(a) you won't see the someProperty like you do with the xmlRequest instance, and this is expected, since it is an inherited property. The reason that you see these properties when you log the xmlRequest is that this object is recieving special treatment.

Luckily you can recreate such a special treatment using chrome custom object formatters, for this to work you have to enable custom formatters from chrome developer tools settings

window.devtoolsFormatters = [{
    header: function(obj){
        if (!(obj instanceof A)){
         return null;
        }
        // to get the exact results like the xmlRequest, you need to remove quotes from the object keys in the json string, can be done with a regex
        return ["div",{}, JSON.stringify(obj.__proto__)]
    },
    hasBody: function(){
        return false;
    }
}]

class A {}
A.prototype.someProperty = 10

console.log(new A()) // {"someProperty":10}