【问题标题】:Why properties of an XMLHttpRequest object are only printable through console.log()?为什么 XMLHttpRequest 对象的属性只能通过 console.log() 打印?
【发布时间】:2018-11-03 12:47:51
【问题描述】:
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(): {}
控制台日志:
XMLHttpRequest {onreadystatechange: null, readyState: 0, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, ...}

如何在 javascript 中创建这样一个对象,其属性仅使用console.log(obj) 打印,但不由上述任何函数返回?

我已经尝试使用constructor functionobject.create()(和enumerable:false)、Object.assign()、使用getters、从类实例化、从扩展类实例化等创建对象

【问题讨论】:

  • 嗨@Marinos 请检查this fiddle。这就是你想要的吗?
  • 嗨@JaydeepMor。没有。
  • 最接近的是继承的 getter,即Object.create({get test() { return 1; }})。但是不,XHR 是一个宿主对象,控制台可以随心所欲地使用它。
  • @Bergi 确实主机对象正在接受浏览器的特殊处理,但是可以使用 chrome 对象自定义格式化程序复制这种处理

标签: javascript ecmascript-6 xmlhttprequest prototype prototypal-inheritance


【解决方案1】:

你可以这样做:

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

具体来说,enumerablethingie 隐藏了该属性。

欲了解更多信息,请参阅此页面: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

【讨论】:

  • 在这种情况下,Object.getOwnPropertyNames() 返回非空数组,带有属性
  • 在更新的情况下,console.log() 也不会打印对象属性。而在我的示例中,console.log() 打印对象属性。
【解决方案2】:

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)

【讨论】:

  • 尝试在 chrome 和 firefox 的控制台中复制/粘贴代码。 console.log 不打印任何内容,与 XMLHttpRequest 相比
  • 我只是猜测它是浏览器在打印内置对象时正在做一些额外的工作。像数组一样也是对象,但打印方式不同。
【解决方案3】:

这是因为原型在 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)

你会看到:

【讨论】:

  • 提供的代码不会重现我的示例中的输出,其中console. log() 打印了一个非空对象。
  • 因为我没有添加自己的属性。
【解决方案4】:

您可以使用名为 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"}

【讨论】:

  • 我不认为我们有任何直接的方法可以将属性从符号转换为字符串,但是我们可以重载 toString 方法并再次返回属性名称。
  • 你能用你建议的内容更新代码吗?如果console.log()XMLHttpRequest 一样打印对象属性,我想答案将值得赏金。
  • 我用一种新方法更新了代码。现在你只需要调用 toString 方法(或任何方法)就可以了。
  • XHR不同。 XHR 实例没有符号。在这段代码中,如果必须访问 name 属性,就像dummyObject1.toString().namedummyObject1.name 会给出undefined
  • 这与符号或常规键无关,这更多是关于实例属性与继承属性的关系。
【解决方案5】:

这与 WhatWG console.log specification 有关:

实现如何打印 args 取决于实现,但实现应该用空格或类似的东西分隔对象,因为这已成为开发人员的期望。

规范使输出格式非常模糊,由实现决定打印什么。

【讨论】:

    【解决方案6】:

    为什么 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
    });
    

    【讨论】:

    • 谢谢回复:还是Object.getOwnPropertyNames()打印的
    【解决方案7】:

    我认为这是最实用的方法。它会帮助你

    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)
    

    这是 Chromium 上编译版本的结果

    【讨论】:

    • 谢谢你的回答。我仍然可以看到控制台上的 name 属性。问题指出,每个控制台日志都不应打印除 console.log(obj) 之外的任何属性,就像 XMLHttpRequest 对象发生的那样
    • 对不起,我修好了。我用私有字段构建了一个 js6 类。您还可以看到 js5 格式,它是 es6 编译结果
    • 但是现在console.log(obj) 不打印任何东西。
    • 你能告诉我你用什么浏览器测试吗?
    • 铬。仅供参考:我执行到console.log(obj)
    【解决方案8】:

    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) 时,您不会像使用 xmlRequest 实例那样看到 someProperty,这是预期的,因为它是一个继承的属性。您在记录 xmlRequest 时看到这些属性的原因是该对象正在接受特殊处理。

    幸运的是,您可以使用 chrome custom object formatters 重新创建这种特殊处理,为此您必须从 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}
    
    

    【讨论】:

    • 感谢您的回答:我已将您的代码粘贴到sn-p,但是执行时,它不打印{"someProperty":10}
    • @MarinosAn 你是否从 chrome 开发工具的设置中启用了 chrome 自定义格式化程序?
    【解决方案9】:

    如何在 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(): {}
    控制台日志:
    代理 {prop1: "value1", prop2: "value2"}

    console.log(obj.prop1) / obj.prop1="newValue" 仍然有效。

    【讨论】:

    • 有趣。这可能是最接近 xhr 主机对象所做的事情。
    【解决方案10】:

    这应该可以解决问题。

    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);
    

    【讨论】:

    • 您在哪个浏览器和版本上试用过此功能?在我的 chrome 和 firefox 上,我最后得到console.log: {}
    • 我在chrome上试过了,你也扩展了最后一个对象吗?由于这是一个 get 属性 chrome 不会自动打印它,您可以展开对象并调用属性 getter。
    • 我知道,但是对于 XMLHttpRequest 实例,您不需要这样做,这就是问题所在。
    • 我在 edge 中尝试过,默认情况下它确实显示了该值。正如其他答案也提到的,这可以是特定于浏览器的实现。
    猜你喜欢
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 2016-07-31
    • 1970-01-01
    • 2020-01-02
    • 2022-01-25
    • 2014-07-28
    相关资源
    最近更新 更多