Почему свойства объекта XMLHttpRequest доступны для печати только через console.log()?

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, …}

Как я могу создать такой объект в javascript, свойства которого выводятся только с помощью console.log(obj), но не возвращаются ни одной из вышеперечисленных функций?

Я уже пытался создавать объекты, используя constructor function, object.create()enumerable:false), Object.assign(), используя getters, создавая из класса, создавая из расширенного класса и т.д.


person Marinos An    schedule 24.05.2018    source источник
comment
Привет @Marinos Пожалуйста, проверьте эту скрипку. Это то, что вы хотите?   -  person Jaydeep Mor    schedule 05.02.2020
comment
Привет @JaydeepMor. Нет.   -  person Marinos An    schedule 05.02.2020
comment
Самое близкое, что вы получите, это унаследованные геттеры, то есть Object.create({get test() { return 1; }}). Но нет, XHR — это хост-объект, и консоль может делать с ним все, что захочет.   -  person Bergi    schedule 02.06.2020
comment
@Bergi действительно, хост-объект получает специальную обработку от браузеров, однако такую ​​обработку можно воспроизвести с помощью пользовательских форматировщиков хромированных объектов.   -  person ehab    schedule 03.06.2020
comment
Спустя долгое время я также нашел близкий ответ   -  person Marinos An    schedule 03.06.2020


Ответы (10)


Это связано с спецификацией WhatWG console.log:

То, как реализация выводит аргументы, зависит от реализации, но реализации должны разделять объекты пробелом или чем-то подобным, поскольку это стало ожиданием разработчика.

Спецификация оставляет формат вывода очень расплывчатым, и реализация решает, что печатать.

person Ján Jakub Naništa    schedule 29.05.2020

Вы можете использовать новый тип данных под названием Symbol для создания ключей для объекта JavaScript.

Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Я приложил пример кода, и вы можете увидеть вывод в консоли.

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"}

person Jasdeep Singh    schedule 05.02.2020
comment
Я не думаю, что у нас есть прямой способ преобразовать свойства из символа в строку, но мы можем перегрузить метод toString и снова вернуть имена свойств. - person Jasdeep Singh; 05.02.2020
comment
Можете ли вы обновить код с тем, что вы предложили. Если console.log() печатает свойства объекта, как в случае XMLHttpRequest, я думаю, ответ будет стоить награды. - person Marinos An; 05.02.2020
comment
Я обновил код с новым подходом. Теперь вам просто нужно вызвать метод toString (или любой другой метод), и он заработает. - person Jasdeep Singh; 05.02.2020
comment
Спасибо за обновление, но в этом решении необходимо явно вызвать toString(). Плюс после вызова Object.keys(dummyObject) и остальные функции начинают печатать свойство. - person Marinos An; 05.02.2020
comment
Это не то же самое, что XHR. Экземпляр XHR не имеет символов. В этом коде, если нужно получить доступ к свойству имени, это будет похоже на dummyObject1.toString().name, а dummyObject1.name даст undefined. - person vatz88; 06.02.2020
comment
@MarinosAn Я пробовал и проверял, но не смог решить. Мне кажется, что это какая-то внутренняя реализация XMLHttpRequest, даже объекты NodeList ведут себя одинаково. Сообщит, если что-нибудь найдет. - person Jasdeep Singh; 17.02.2020
comment
Это не связано с символами или обычными ключами, это больше касается свойств экземпляра по сравнению с унаследованными свойствами. - person ehab; 03.06.2020

Все свойства экземпляра XMLHttpRequest являются унаследованными свойствами, я имею в виду, что они существуют не в самом экземпляре, а в его цепочке прототипов.

Это объясняет, почему

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

Вы можете создать такой объект просто так

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)) // {}

однако при попытке console.log(a) вы не увидите someProperty, как в случае с экземпляром xmlRequest, и это ожидаемо, поскольку это унаследованное свойство. Причина, по которой вы видите эти свойства при регистрации xmlRequest, заключается в том, что этот объект получает специальную обработку.

К счастью, вы можете воссоздать такую ​​особую обработку, используя программы форматирования пользовательских объектов Chrome, чтобы это работало, вы должны включить пользовательские средства форматирования в настройках инструментов разработчика Chrome.

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}

person ehab    schedule 03.06.2020
comment
Спасибо за ответ: я вставил ваш код в фрагмент, но при выполнении он не печатает {"someProperty":10} - person Marinos An; 03.06.2020
comment
@MarinosAn Вы включили пользовательские средства форматирования Chrome в настройках инструментов Chrome dev? - person ehab; 03.06.2020

Как я могу создать такой объект в javascript, свойства которого выводятся только с помощью console.log(obj), но не возвращаются ни одной из вышеперечисленных функций?

Одно близкое решение, которое я нашел, приведено ниже. Это напечатает свойства с console.log(obj). Однако obj является прокси.

На консоли Chrome результат такой же, как и вывод XMLHttpRequest (с классом Proxy). В консоли firefox это представление класса Proxy, так что это не совсем то же самое.


    const obj = new Proxy({prop1:"value1", prop2: "value2"}, {ownKeys: _=>[]});

    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(obj)

результат:

Object.keys(): []
Object.getOwnPropertyNames(): []
Object.entries(): []
JSON.stringify(): {}
console.log:
Прокси {prop1: "value1", prop2: "value2"}

и console.log(obj.prop1) / obj.prop1="newValue" все еще работает.

person Marinos An    schedule 03.06.2020
comment
Интересно. Это, вероятно, наиболее близко к тому, что делает хост-объект xhr. - person Bergi; 03.06.2020

Object.keys: дает только собственное перечисляемое свойство объекта.

Object.getOwnPropertyNames: дает перечисляемые и неперечисляемые свойства, найденные непосредственно в данном объекте.

Object.entries: задайте собственные перечисляемые пары свойств объекта со строковыми ключами.

JSON.stringify: рассматривает только перечисляемые свойства объектов.


Создание собственного такого объекта

Вы можете использовать Object.create. который создаст новый объект, используя существующий объект в качестве прототипа вновь созданного объекта.

obj1 = {
  key1: 'some key'
};

var obj = Object.create(obj1);


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)

person vatz88    schedule 04.02.2020
comment
Попробуйте скопировать/вставить код в консоли chrome и firefox. console.log ничего не печатает, в отличие от XMLHttpRequest - person Marinos An; 04.02.2020
comment
Я просто предполагаю, что это браузер, который выполняет дополнительную работу, когда печатает встроенные объекты. Как и массивы, они тоже являются объектами, но печатаются по-другому. - person vatz88; 04.02.2020

Это потому, что прототипирование работает в Javascript, это настоящая причина.

Когда вы создаете экземпляр объекта из прототипа, свойства внутри __proto__ не будут отображаться (не перечисляемые свойства)

Например, попробуйте:

function MyXMLHttpRequest(){};
MyXMLHttpRequest.prototype.name = "Edward"
var obj = new MyXMLHttpRequest();

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)
console.log(obj.name)

Ты увидишь:

выводит консоль

person Alejandro Molina    schedule 04.02.2020
comment
Предоставленный код не воспроизводит вывод в моем примере, в котором console. log() печатает непустой объект. - person Marinos An; 05.02.2020
comment
Потому что мне не добавили собственные свойства. - person Alejandro Molina; 05.02.2020

Почему свойства объекта XMLHttpRequest доступны для печати только через console.log()?

Это не правда

var obj = new XMLHttpRequest();
for(var key in obj) console.log(key);

Ответ на вопрос из описания

Как я могу создать такой объект в javascript, свойства которого выводятся только с помощью

  var obj = Object.create({ foo: "foo", constructor: function() {} });

Если вы хотите скрыть свойство от for in, вы должны установить для атрибута enumerable значение false для желаемого свойства:

Object.defineProperty(obj, 'foo', {
  value: 42,
  enumerable: false
});
person maksimr    schedule 02.06.2020
comment
Спасибо за ответ: он все еще печатается Object.getOwnPropertyNames() - person Marinos An; 03.06.2020

Я думаю, что это самый практичный способ. Это поможет вам

class User {
  //this is a private field
  #name
  get Name() {
    return this.#name
  }
  set Name(val) {
    this.#name = val
  }
}

var obj = new User()
obj.Name = 'test'

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)
console.log(obj.Name)

это его скомпилированный код

function _classPrivateFieldSet(receiver, privateMap, value) {
  var descriptor = privateMap.get(receiver)
  if (!descriptor) {
    throw new TypeError('attempted to set private field on non-instance')
  }
  if (descriptor.set) {
    descriptor.set.call(receiver, value)
  } else {
    if (!descriptor.writable) {
      throw new TypeError('attempted to set read only private field')
    }
    descriptor.value = value
  }
  return value
}
function _classPrivateFieldGet(receiver, privateMap) {
  var descriptor = privateMap.get(receiver)
  if (!descriptor) {
    throw new TypeError('attempted to get private field on non-instance')
  }
  if (descriptor.get) {
    return descriptor.get.call(receiver)
  }
  return descriptor.value
} // Try edit message
class User {
  constructor() {
    _name.set(this, { writable: true, value: void 0 })
  }
  get Name() {
    return _classPrivateFieldGet(this, _name)
  }
  set Name(val) {
    _classPrivateFieldSet(this, _name, val)
  }
}
var _name = new WeakMap()
var obj = new User()
obj.Name = 'test'
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)
console.log(obj.Name)

это результат скомпилированной версии на хроме введите здесь описание изображения

person Ehsan Nazeri    schedule 03.06.2020
comment
Вам за ответ. Я все еще вижу свойство имени на консоли. В вопросе говорится, что каждый журнал консоли не должен печатать никаких свойств, кроме console.log(obj), как это происходит с объектом XMLHttpRequest. - person Marinos An; 03.06.2020
comment
извините, исправил. Я создаю класс js6 с частным полем. вы также можете увидеть формат js5, который является результатом компиляции es6 - person Ehsan Nazeri; 03.06.2020
comment
Но теперь console.log(obj) ничего не печатает. - person Marinos An; 03.06.2020
comment
не могли бы вы сказать мне, какой браузер вы используете для тестирования? - person Ehsan Nazeri; 03.06.2020
comment
Хром. К вашему сведению: я выполняю до console.log(obj) - person Marinos An; 03.06.2020
comment
попробуйте скомпилированный, результаты такие же? - person Ehsan Nazeri; 03.06.2020
comment
Скомпилированный код был тем, который я использовал. Проверено также на firefox. Последняя строка: console.log: Object { }, которая не содержит свойства. Я не выполняю console.log(obj.Name), потому что это не в контексте вопроса. - person Marinos An; 03.06.2020

Это должно сработать.

var obj = Object.create({
    get name(){
        return 'I go only to console.log()';
    }
});
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:", obj);
person Shubham Agrawal    schedule 03.06.2020
comment
В каком браузере и версии вы это пробовали? В моем Chrome и Firefox я получаю console.log: {} в конце. - person Marinos An; 03.06.2020
comment
Я пробовал на хроме, ты тоже последний объект расширил? Поскольку это свойство получения, хром не печатает его автоматически, вы можете развернуть объект и вызвать средство получения свойства. - person Shubham Agrawal; 03.06.2020
comment
Я знаю, но с экземпляром XMLHttpRequest вам не нужно этого делать, и именно об этом вопрос. - person Marinos An; 03.06.2020
comment
Я пробовал в Edge, и он показывает значение по умолчанию. Как уже упоминалось в других ответах, это может быть конкретная реализация браузера. - person Shubham Agrawal; 03.06.2020

Вы можете сделать что-то вроде этого:

Object.defineProperty(obj, 'key', {
    enumerable: false,
    value: '123'
});

В частности, enumerable штучка скрывает свойство.

Для получения дополнительной информации см. эту страницу: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

ОБНОВЛЕНИЕ:

Следующий подход (улучшенная версия вышеизложенного), кажется, скрывает свойства от всех упомянутых проверок, хотя он немного многословен:

var proto = {};
Object.defineProperty(proto, "key", {
    get: function () { return 123; }
});

var obj = Object.create(proto);

console.log(obj); // {}
console.log(Object.getOwnPropertyNames(obj)); // []
console.log(obj.key); // 123
person alx    schedule 24.05.2018
comment
В этом случае Object.getOwnPropertyNames() возвращает непустой массив со свойствами - person Marinos An; 24.05.2018
comment
В обновленном случае console.log() также не печатает свойства объекта. Хотя в моем примере console.log() печатает свойства объекта. - person Marinos An; 28.05.2018