8

所以,我在玩 Proxy 对象,在尝试了解它们如何与扩展语法和解构混合时,我偶然发现了这种奇怪的行为:

const obj = {
  origAttr: 'hi'
}

const handler = {
  get(target, prop) {
    console.log(prop);
    return 1;
  },
  has(target, prop) {
    return true;
  },
  ownKeys(target) {
    return [...Reflect.ownKeys(target), 'a', 'b'];
  },
  getOwnPropertyDescriptor(target, key) {
    return {
      enumerable: true,
      configurable: true
    };
  }
}

const test = new Proxy(obj, handler);
const testSpread = { ...test};

console.log('Iterate test');
// Works OK, output as expected
for (const i in test) {
  console.log(i, ' -> ', test[i]);
}

console.log('Iterate testSpread');
// Also works OK, output as expected
for (const i in testSpread) {
  console.log(i, ' -> ', testSpread[i]);
}

console.log('Here comes the unexpected output from console.log:');
console.log(test); // All attributes are 'undefined'
console.log(testSpread); // This is OK for some wierd reason

上述脚本输出(在节点 v10.15.1 上):

这是来自控制台日志的意外输出:

Symbol(nodejs.util.inspect.custom)
Symbol(Symbol.toStringTag)
Symbol(Symbol.iterator)
{ origAttr: undefined, a: undefined, b: undefined }
{ origAttr: 1, a: 1, b: 1 }

为什么 console.log(test); 输出显示对象的属性都是未定义的?如果在调试某些东西时发生,这可能会引起一些严重的头痛。

它是节点本身的错误还是console.log的实现中的错误?

4

2 回答 2

2

好的,我做了更多的挖掘工作,并将整个事情追溯到在我的代理对象上调用 Object.getOwnPropertyDescriptor 以获取其属性的值。

但是在我的情况下,“值”属性显然是未定义的,因为我有一个 getOwnPropertyDescriptor 陷阱,它只指定可枚举和可配置的属性(因此可以迭代数组,将它与扩展运算符一起使用等等)。由于没有从 getOwnPropertyDescriptor 陷阱调用 get 陷阱的标准方法,恕我直言,这实际上无法修复。不过被证明是错误的会很有趣:)

好吧,正如Bergi 在评论中指出的那样,有一种标准方法。

同样在文档https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Parameters “这绑定到处理程序”

编辑我的代码以反映这一点。

演示 getOwnPropertyDescriptor 行为的代码如下:

const obj = {
  origAttr: 'hi'
}

const handler = {
  get(target, prop) {
    return 1;
  },
  has(target, prop) {
    return true;
  },
  ownKeys(target) {
    return [...Reflect.ownKeys(target), 'a', 'b'];
  },
  getOwnPropertyDescriptor(target, key) {
    return {
      value: this.get(target, key),
      enumerable: true,
      configurable: true
    };
  }
}

const test = new Proxy(obj, handler);
const testSpread = { ...test
};

// Defined, due to trapped getOwnPropertyDescriptor which returns a value attribute
console.log(Object.getOwnPropertyDescriptor(test, 'origAttr'))

// Defined, because it is a regular object, not a proxy with a getOwnPropertyDescriptor trap
console.log(Object.getOwnPropertyDescriptor(testSpread, 'origAttr'))

于 2019-03-12T10:43:34.947 回答
1

the Proxy object, as per definition Proxy is nothing but virtualisation of the object you are proxying with it.

Therefore the Proxy object itself has only the attributes of the object you are proxying, if you tried to run console.log(test) you will see the console will print out Proxy {origAttr: "hi"} but it will also have a handler and a target inside that you defined above.

When you instead use the spread operator you are creating a new object which it's created in the same way you would by iterating on the properties from your Proxy object like this:

Object.keys(test) --> ["origAttr", "a", "b"] because that's what you defined within ownKeys(target) { return [...Reflect.ownKeys(target), 'a', 'b']; }.

Then it will access test["origAttr"], then test["a"] and test["b"] using the proxy get function, which returns always 1.

As a result you object testSpread actually contains these attributes, while test does not.

And when you run console.log(testSpread) --> {origAttr: 1, a: 1, b: 1}

于 2019-03-11T16:45:28.813 回答