Search code examples
javascriptobjectprototype

Accessing a object's non enumerable properties


Following this question: Object.prototype returns empty object in Node which asked

Why doesn't console.log(Object.prototype) log the expected object and instead returns {}.

Here was the answer:

It is because the console.log [...] uses Object.keys() on objects, and it returns enumerable properties only. And Object.prototype contains non-enumerable properties, that is why it returns an empty node.

- Amrender Singh


I was wondering how I could output all properties of a given object (for instance Object.prototype). As shown by another answer, using getOwnPropertyNames works fine:

const ObjectPrototype = Object.getOwnPropertyNames(Object.prototype).reduce((object, name) => {

  object[name] = Object.prototype[name];

  return object;

}, {});

console.log(
  ObjectPrototype
);
.as-console-wrapper { max-height: 100% !important; top: 0; }

I thought I could extract the hidden properties of a given object this way. But actually, it doesn't fully work.

Here's an easier example: the object I'm inspecting is the object {mykey: 'value'}. Chrome gives me its property (console.log does too) but also its prototype, __proto__ greyed out:

enter image description here

let object = { mykey: 'value' };

object = Object.getOwnPropertyNames(object).reduce((acc, name) => {

  acc[name] = object[name];

  return acc;

}, {});

console.log(
  object
);
.as-console-wrapper { max-height: 100% !important; top: 0; }

The above only returns one property. I was expecting to also have __proto__. Is there a way to show all of one's properties including hidden ones? Or do I manually have to access it: i.e. reduce Object.getOwnPropertyNames(object.__proto__)?


PS: or in other words, how does Chrome handle hidden properties?


Solution

  • You can write a function, let's call it getAllPropertyNames(), that iterates through the prototype chain of the object and accumulates the properties of each level:

    function getAllPropertyNames (o) {
      let propertyNames = []
    
      for (let proto = o; proto !== null; proto = Object.getPrototypeOf(proto)) {
        propertyNames = propertyNames.concat(Object.getOwnPropertyNames(proto))
      }
    
      return propertyNames
    }
    
    console.log(getAllPropertyNames({ mykey: 'value' }))

    These properties aren't hidden, they're just inherited, which is why getOwnPropertyNames() does not include them. They're not specific to the object, but rather everything that inherits from Object.prototype.

    Update

    I'm going to attempt something a little unconventional, but should produce the desired output:

    // probably could be named better
    // feel free to suggest one in comments
    function asEnumerable (o) {
      const acc = Object.create(null)
    
      for (let oProto = o, aProto = acc; oProto !== null; oProto = Object.getPrototypeOf(oProto), aProto = aProto.__proto__) {
        for (const key of Object.getOwnPropertyNames(oProto)) {
          aProto[key] = oProto[key]
        }
    
        if (Object.getPrototypeOf(oProto) !== null) {
          aProto.__proto__ = Object.create(null)
        }
      }
    
      return acc
    }
    
    console.log(asEnumerable({ mykey: 'value' }))
    .as-console-wrapper{min-height:100%!important}

    This implementation does abide to the specification, since it does not treat __proto__ as if it modifies inheritance of the object, but simply as a normal property on acc and aProto.