Search code examples
javascriptecmascript-6es6-proxy

Why isn't ownKeys Proxy trap working with Object.keys()?


In the documentation of the Proxy ownKeys trap on MDN it states that it will intercept Object.keys() calls:

This trap can intercept these operations:

Object.getOwnPropertyNames()

Object.getOwnPropertySymbols()

Object.keys()

Reflect.ownKeys()

However, from my tests it doesn't seem to work with Object.keys:

const proxy = new Proxy({}, {
  ownKeys() {
    console.log("called")
    return ["a", "b", "c"]
  }
})

console.log(Object.keys(proxy))

console.log(Object.getOwnPropertyNames(proxy))

console.log(Reflect.ownKeys(proxy))


Solution

  • The reason is simple: Object.keys returns only properties with the enumerable flag. To check for it, it calls the internal method [[GetOwnProperty]] for every property to get its descriptor. And here, as there’s no property, its descriptor is empty, no enumerable flag, so it’s skipped.

    For Object.keys to return a property, we need it to either exist in the object, with the enumerable flag, or we can intercept calls to [[GetOwnProperty]] (the trap getOwnPropertyDescriptor does it), and return a descriptor with enumerable: true.

    Here’s an example of that:

    let user = { };
    
    user = new Proxy(user, {
      ownKeys(target) { // called once to get a list of properties
        return ['a', 'b', 'c'];
      },
    
      getOwnPropertyDescriptor(target, prop) { // called for every property
        return {
          enumerable: true,
          configurable: true
          /* ...other flags, probable "value:..." */
        };
      }
    
    });
    
    console.log( Object.keys(user) ); // ['a', 'b', 'c']

    Source