有以下对象:
let obj = { id: 0 };
以及以下Proxy
:
let objProxy = new Proxy(obj, {
get: (target, name) => {
if (name == "id")
return "id from proxy";
}});
是否可以在Proxy
之后“保留”Object.assign()
(或者对象扩展运算符,其中afaik只是Object.assign()
的语法糖)?
let objProxyNew = Object.assign({}, objProxy); // i.e. {...objProxy};
以便objProxyNew.id
返回"id from proxy"
?
答案 0 :(得分:2)
好像我是完全相同的问题的第三个人,这是我发现的关于stackoverflow的最接近的问题,但是它没有真正的答案,所以我必须自己进行调查。
顺便说一句,Philip在他的示例中想要的行为是默认行为,因此无需进行任何更改:
let obj = { id: 0 };
let objProxy = new Proxy(obj, {
get: (target, name) => {
if (name == "id")
return "id from proxy";
}});
let objProxyNew = Object.assign({}, objProxy);
console.log(objProxyNew.id); // "id from proxy"
但这仅适用于简单的代理,其中代理对象的属性名称与最终对象的属性名称相同。
{...obj}
让我们举一个更复杂的示例,一个“ zip”操作的代理(将键和值的单独数组组合到单个对象中):
let objProxy = new Proxy({
keys: ["a", "b", "c", "d"],
values: [1, 3, 5, 7]
}, {
get(target, name) {
var index = target.keys.indexOf(name);
return index >= 0 ? target.values[target.keys.indexOf(name)] : false
}
});
console.log(objProxy.c); // 5
console.log({...objProxy}); // {keys: undefined, values: undefined}
现在,我们从原始对象中获得了属性,但是没有它们的值,因为代理对于“键”和“值”属性什么也不返回。
正如我所发现的,之所以发生这种情况,是因为我们尚未为“ ownKeys”定义陷阱,并且默认情况下调用了Object.getOwnPropertyNames(target)
。
扩展代理:
ownKeys(target) { return target.keys; }
甚至更糟,因为现在根本没有克隆任何属性:
console.log({...objProxy}); // {}
现在正在发生的事情是Object.assign为“ ownKeys”函数返回的每个键调用Object.getOwnPropertyDescriptor
。默认情况下,从“目标”中检索属性描述符,但是我们可以使用另一个名为“ getOwnPropertyDescriptor”的陷阱再次更改它:
let objProxy = new Proxy({
keys: ["a", "b", "c", "d"],
values: [1, 3, 5, 7]
}, {
get(target, name) {
var index = target.keys.indexOf(name);
return index >= 0 ? target.values[index] : false
},
ownKeys(target) {
return target.keys;
},
getOwnPropertyDescriptor(target, name) {
return { value: this.get(target, name), configurable: true, enumerable: true };
}
});
enumerable
控制哪些属性将被克隆并在控制台中可见。
必须为代理属性设置configurable
,否则会出现错误:
VM1028:1代理:陷阱上未捕获的TypeError:'getOwnPropertyDescriptor' 报告的属性“ a”的不可配置性为 代理目标中不存在或不可配置 在:1:1
我们还需要将“ writable”设置为“ true”才能在严格模式下设置属性。
value
似乎没有被Object.assign
使用,但是可能被其他一些框架或实现所使用。如果实际获取值成本很高,我们可以将其定义为获取方法:
get value() { return this.get(target, name); }
要支持in
运算符并实现一致的实现,我们还应该实现“具有”陷阱。因此最终的实现如下所示:
let objProxy = new Proxy({
keys: ["a", "b", "c", "d"],
values: [1, 3, 5, 7]
}, {
get(target, name) {
var index = target.keys.indexOf(name);
return index >= 0 ? target.values[index] : false
},
ownKeys: (target) => target.keys,
getOwnPropertyDescriptor(target, name) {
const proxy = this;
return { get value() { return proxy.get(target, name); }, configurable: true, enumerable: true };
},
has: (target, name) => target.keys.indexOf(name) >= 0
});
console.log({...objProxy}); // {a: 1, b: 3, c: 5, d: 7}
[...obj]
另一个故事是支持[...objProxy]
-在这里,我们需要在吸气剂中定义[Symbol.iterator]
:
let objProxy = new Proxy({
values: [1, 2, 3, 4],
delta: [9, 8, 7, 6]
}, {
get(target, name){
if (name === Symbol.iterator) {
return function*() {
for (let i = 0; i < target.values.length; i ++) { yield target.values[i] + target.delta[i]; }
}
}
return target.values[name] + target.delta[name];
}
});
console.log([...objProxy]); // [10, 10, 10, 10]
我们还可以将“ Symbol.iterator”代理到原始对象:
return () => target.values[Symbol.iterator]();
或
return target.values[Symbol.iterator].bind(target.values);
我们需要重新绑定原始上下文,因为否则将对代理对象执行迭代器