为什么 ownKeys 代理陷阱不能与 Object.keys() 一起使用?

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

documentation of the Proxy ownKeys trap on MDN 中声明它将拦截 Object.keys() 调用:

This trap can intercept these operations:

Object.getOwnPropertyNames()

Object.getOwnPropertySymbols()

Object.keys()

Reflect.ownKeys()

但是,根据我的测试,它似乎不适用于 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))

是MDN错了,还是我做错了什么?

原因很简单:Object.keys returns 只有带有可枚举标志的属性。为了检查它,它为每个 属性 调用内部方法 [[GetOwnProperty]] 以获取其描述符。而这里,由于没有属性,它的描述符是空的,没有可枚举标志,所以跳过。

对于Object.keys到return一个属性,我们需要它存在于对象中,带有可枚举标志,或者我们可以拦截对[[GetOwnProperty]]的调用(陷阱 getOwnPropertyDescriptor 做到了),以及 return 一个带有 enumerable: true 的描述符。

这是一个例子:

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

Object.keys returns 只有对象的 enumerable 自有属性。您的代理没有这样的,或者至少它没有在其 getOwnPropertyDescriptor 陷阱中报告它们。它适用于

const proxy = new Proxy({}, {
  ownKeys() {
    console.log("called ownKeys")
    return ["a", "b", "c"]
  },
  getOwnPropertyDescriptor(target, prop) {
    console.log(`called getOwnPropertyDescriptor(${prop})`);
    return { configurable: true, enumerable: true };
  } 
})

console.log(Object.keys(proxy))

console.log(Object.getOwnPropertyNames(proxy))

console.log(Reflect.ownKeys(proxy))