Javascript Proxy and spread syntax, combined with console.log

允我心安 提交于 2019-12-09 16:40:56

问题


So, I was playing around with Proxy objects and while trying to see how they mix with spread syntax and de-structuring, I stubled upon this weird behavior:

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

The above script outputs (on node v10.15.1):

Here comes the unexpected output from console log:

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

Why does console.log(test); output show that the attributes of the object are all undefined? This could cause some serious headache if it were to happen when debugging something.

Is it a bug in node itself or perhaps in the implementation of console.log?


回答1:


Okay, I did some more digging and traced the whole thing down to Object.getOwnPropertyDescriptor being called on my proxy object to get the values of its attributes.

But the "value" attribute is obviously undefined in my case, since I have a trap for getOwnPropertyDescriptor which only specifies the enumerable and configurable attributes (thus making it possible to iterate the array, use it with spread operators and so on). As there is no standard way to invoke the get trap from the getOwnPropertyDescriptor trap, this can't really be fixed IMHO. Would be interesting to be proven wrong though :)

Well, as Bergi pointed out in the comments, there is a standard way.

Also in the docs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Parameters "this is bound to the handler"

Edited my code to reflect that.

The code demonstrating getOwnPropertyDescriptor behavior is below:

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'))



回答2:


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}



来源:https://stackoverflow.com/questions/55106122/javascript-proxy-and-spread-syntax-combined-with-console-log

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!