Javascript 代理和传播语法,结合 console.log
Javascript Proxy and spread syntax, combined with console.log
所以,我在研究 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 的实现中的错误?
Proxy 对象,根据定义 Proxy 只不过是您使用它代理的对象的虚拟化。
因此 Proxy 对象本身只有你所代理的对象的属性,如果你尝试 运行 console.log(test)
你会看到控制台将打印出 Proxy {origAttr: "hi"}
但是它还将在您上面定义的内部有一个处理程序和一个目标。
当您改为使用扩展运算符时,您正在创建一个新对象,它的创建方式与您通过迭代 Proxy
对象的属性相同,如下所示:
Object.keys(test) --> ["origAttr", "a", "b"]
因为那是你在 ownKeys(target) {
return [...Reflect.ownKeys(target), 'a', 'b'];
}
.
中定义的
然后它将访问 test["origAttr"]
,然后使用代理 get
函数访问 test["a"]
和 test["b"]
,returns 总是 1
。
因此您对象 testSpread
实际上包含这些属性,而 test
不包含。
当你 运行 console.log(testSpread) --> {origAttr: 1, a: 1, b: 1}
好吧,我做了更多的挖掘,并将整个事情追溯到 Object.getOwnPropertyDescriptor 在我的代理对象上被调用以获取其属性的值。
但是 "value" 属性在我的例子中显然是未定义的,因为我有一个 getOwnPropertyDescriptor 的陷阱,它只指定可枚举和可配置的属性(因此可以迭代数组,将它与展开运算符一起使用等等)。 由于没有从 getOwnPropertyDescriptor 陷阱调用 get 陷阱的标准方法,恕我直言,这无法真正解决。不过被证明是错误的会很有趣:)
好吧,正如 Bergi 在评论中指出的那样,有一个标准方法。
也在文档中 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Parameters "this is bound to the handler"
编辑我的代码以反映这一点。
演示 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'))
所以,我在研究 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 的实现中的错误?
Proxy 对象,根据定义 Proxy 只不过是您使用它代理的对象的虚拟化。
因此 Proxy 对象本身只有你所代理的对象的属性,如果你尝试 运行 console.log(test)
你会看到控制台将打印出 Proxy {origAttr: "hi"}
但是它还将在您上面定义的内部有一个处理程序和一个目标。
当您改为使用扩展运算符时,您正在创建一个新对象,它的创建方式与您通过迭代 Proxy
对象的属性相同,如下所示:
Object.keys(test) --> ["origAttr", "a", "b"]
因为那是你在 ownKeys(target) {
return [...Reflect.ownKeys(target), 'a', 'b'];
}
.
然后它将访问 test["origAttr"]
,然后使用代理 get
函数访问 test["a"]
和 test["b"]
,returns 总是 1
。
因此您对象 testSpread
实际上包含这些属性,而 test
不包含。
当你 运行 console.log(testSpread) --> {origAttr: 1, a: 1, b: 1}
好吧,我做了更多的挖掘,并将整个事情追溯到 Object.getOwnPropertyDescriptor 在我的代理对象上被调用以获取其属性的值。
但是 "value" 属性在我的例子中显然是未定义的,因为我有一个 getOwnPropertyDescriptor 的陷阱,它只指定可枚举和可配置的属性(因此可以迭代数组,将它与展开运算符一起使用等等)。 由于没有从 getOwnPropertyDescriptor 陷阱调用 get 陷阱的标准方法,恕我直言,这无法真正解决。不过被证明是错误的会很有趣:)
好吧,正如 Bergi 在评论中指出的那样,有一个标准方法。
也在文档中 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Parameters "this is bound to the handler"
编辑我的代码以反映这一点。
演示 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'))