The ownKeys Trap

The ownKeys proxy trap intercepts the internal method [[OwnPropertyKeys]] and allows you to override that behavior by returning an array of values. This array is used in four methods: the Object.keys() method, the Object.getOwnPropertyNames() method, the Object.getOwnPropertySymbols() method, and the Object.assign() method. (The Object.assign() method uses the array to determine which properties to copy.)

The default behavior for the ownKeys trap is implemented by the Reflect.ownKeys() method and returns an array of all own property keys, including both strings and symbols. The Object.getOwnProperyNames() method and the Object.keys() method filter symbols out of the array and returns the result while Object.getOwnPropertySymbols() filters the strings out of the array and returns the result. The Object.assign() method uses the array with both strings and symbols.

The ownKeys trap receives a single argument, the target, and must always return an array or array-like object; otherwise, an error is thrown. You can use the ownKeys trap to, for example, filter out certain property keys that you don’t want used when the Object.keys(), the Object.getOwnPropertyNames() method, the Object.getOwnPropertySymbols() method, or the Object.assign() method is used. Suppose you don’t want to include any property names that begin with an underscore character, a common notation in JavaScript indicating that a field is private. You can use the ownKeys trap to filter out those keys as follows:

  1. let proxy = new Proxy({}, {
  2. ownKeys(trapTarget) {
  3. return Reflect.ownKeys(trapTarget).filter(key => {
  4. return typeof key !== "string" || key[0] !== "_";
  5. });
  6. }
  7. });
  8. let nameSymbol = Symbol("name");
  9. proxy.name = "proxy";
  10. proxy._name = "private";
  11. proxy[nameSymbol] = "symbol";
  12. let names = Object.getOwnPropertyNames(proxy),
  13. keys = Object.keys(proxy);
  14. symbols = Object.getOwnPropertySymbols(proxy);
  15. console.log(names.length); // 1
  16. console.log(names[0]); // "name"
  17. console.log(keys.length); // 1
  18. console.log(keys[0]); // "name"
  19. console.log(symbols.length); // 1
  20. console.log(symbols[0]); // "Symbol(name)"

This example uses an ownKeys trap that first calls Reflect.ownKeys() to get the default list of keys for the target. Then, the filter() method is used to filter out keys that are strings and begin with an underscore character. Then, three properties are added to the proxy object: name, _name, and nameSymbol. When Object.getOwnPropertyNames() and Object.keys() is called on proxy, only the name property is returned. Similarly, only nameSymbol is returned when Object.getOwnPropertySymbols() is called on proxy. The _name property doesn’t appear in either result because it is filtered out.

I> The ownKeys trap also affects the for-in loop, which calls the trap to determine which keys to use inside of the loop.