7

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?

Community
  • 1
  • 1
Ivan
  • 34,531
  • 8
  • 55
  • 100
  • What you are looking for is a foreach loop, see this thread: https://stackoverflow.com/questions/7440001/iterate-over-object-keys-in-node-js – Robert Page Jul 10 '18 at 14:32
  • At least on Chrome, my update should output `object` almost exactly as the developer console does. – Patrick Roberts Jul 10 '18 at 14:53
  • You cannot access hidden properties, that's why they are *hidden*. Chrome shows it because the debugger is more powerful than what JS alone can do, and it shows it with the confusing name `__proto__` suggesting that it's a normal property. (That said, you can access the prototype through `Object.getPrototypeOf`, but it's not possible in general to access hidden properties). – Bergi Jul 10 '18 at 15:10

2 Answers2

1

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.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Just use `Object.getPrototypeOf` instead of the deprecated `__proto__` – Bergi Jul 10 '18 at 15:09
  • @Bergi `Object.getPrototypeOf` does not allow `__proto__` to be reconfigured as `enumerable`. Since I'm using `Object.defineProperty(..., '__proto__', ...)` in that example already, there's no point in attempting to avoid it in other cases. – Patrick Roberts Jul 10 '18 at 15:19
  • In an object that doesn't inherit from `Object.prototype`, you can use `__proto__` like any other property, you don't even need to make it enumerable explicitly. And it's not the deprecated `__proto__` getter/setter, which you still should avoid. – Bergi Jul 10 '18 at 15:28
0

As mentioned above, can make ues of recursion

// function to print all attributes for some object 
function getPropertyNames (Obj, prev = []) {
  if (Obj) {
    return getPropertyNames(
      Obj.prototype || Obj.__proto__, 
      prev.concat(Object.getOwnPropertyNames(Obj))
    )
  }
  return prev.sort()
}
Mr. Hydra
  • 3
  • 4