Gothdo Gothdo - 1 month ago 7
Javascript Question

Why does Object.keys() and Object.getOwnPropertyNames() produce different output when called on a Proxy object with ownKeys handler?

I have the following proxy:

const p = new Proxy({}, {
ownKeys(target) {
return ['a', 'b'];
},
});


MDN says that:


This trap can intercept these operations:


  • Object.getOwnPropertyNames()

  • Object.getOwnPropertySymbols()

  • Object.keys()

  • Reflect.ownKeys()




Therefore, I expected
Object.getOwnPropertyNames()
and
Object.keys()
to produce the same output. However,
Object.getOwnPropertyNames(p)
returns
['a', 'b']
(as expected), but
Object.keys(p)
returns an empty array. Why is that?

Also, if I add a property to this object which is not returned by the
ownKeys
handler (for example
c
), it gets ignored by both functions (they don't change their output). However, when I add a property which is returned by the
ownKeys
handler (for example
a
),
Object.keys(p)
now returns
['a']
.

Code snippet:



const p = new Proxy({}, {
ownKeys(target) {
return ['a', 'b'];
},
});

console.log(Object.getOwnPropertyNames(p)); // ['a', 'b']
console.log(Object.keys(p)); // []

p.c = true;
console.log(Object.getOwnPropertyNames(p)); // ['a', 'b']
console.log(Object.keys(p)); // []

p.a = true;
console.log(Object.getOwnPropertyNames(p)); // ['a', 'b']
console.log(Object.keys(p)); // ['a']




Answer

The difference between Object.keys() and Object.getOwnPropertyNames() is that Object.keys() will invoke [[GetOwnProperty]] on the object, and only add the property to the result list if the returned property descriptor is enumerable. Since the object doesn't have a such a property, [[GetOwnProperty]] will return undefined and the property (name) is ignored.

You can overwrite/implement [[GetOwnProperty]] in the proxy by implementing getOwnPropertyDescriptor:

const p = new Proxy({}, {
  ownKeys(target) {
    return ['a', 'b'];
  },
  getOwnPropertyDescriptor(k) {
    return {
      enumerable: true,
      configurable: true,
    };
  }
});

console.log(Object.getOwnPropertyNames(p)); // ['a', 'b']
console.log(Object.keys(p)); // ['a', 'b']