18

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']
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177

2 Answers2

28

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']
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • At `javascript` at Answer the values are only returned by the handlers, not set at the `target` object, yes? – guest271314 Nov 01 '16 at 01:12
  • No actual property appears to be set at `target` object `{}`; though `{}` does not have an identifier outside of `Proxy()` call, could use parameter of function to reference target object. The array or object are only `return`ed by function, not set at `target` `{}`? – guest271314 Nov 01 '16 at 01:15
  • @FelixKling so, following your line of thought one should be able to list a and b if they were defined using `Object.defneProperty` and setting it to enumerable, right? I tried that and didn't work. Any idea why? – dorphalsig Sep 04 '17 at 22:47
16

The difference between Object.keys and Object.getOwnPropertyNames is that the latter returns own properties regardless of whether or not they are enumerable.

The properties added to the object by Proxy are non-enumerable, and won't show up in Object.keys, but will in Object.getOwnPropertyNames

Generally, properties added by assignment or a property initializer are enumerable by default, and properties added with methods such as Object.assign, new Proxy etc. are non-enumerable by default.

There's more on property ownership and enumerability on MDN, where one can also find this table that outlines the difference in the two methods

enter image description here

adeneo
  • 312,895
  • 29
  • 395
  • 388