同一目标的多个代理对象的明显污染
Apparent pollution with multiple Proxy objects for the same target
我正在尝试为 JavaScript 中的同一个目标对象创建多个代理包装器,每个包装器的属性略有不同,这些属性会影响包装功能的运行方式。这些属性被分配给 receiver
对象并从 set
和 get
处理程序中访问。但是,当我检查生成的代理时,它们都具有 属性 集,我希望它们已分配给创建的 last 代理。
const obj = {};
const proxies = ['one', 'two'].map(name => {
console.log(`proxy ${name}`);
const proxy = new Proxy(obj, {
get: (target, prop, receiver) => {
if (prop === 'name') { return receiver.name; }
return target[prop];
},
set: (target, prop, val, receiver) => {
if (prop === 'name') {
console.log(`setting name ${val} on receiver`);
Object.defineProperty(receiver, prop, {
value: val,
configurable: true,
enumerable: true}
);
} else {
console.log(`setting ${prop} ${val} on target`);
target[prop] = val;
}
return true;
}
});
proxy.name = name;
return proxy;
});
console.log();
console.log(proxies);
我的预期结果:[{name: 'one'}, {name: 'two'}]
.
实际结果:[{name: 'two'}, {name: 'two'}]
。尽管它们看起来相同,但它们并非严格相等。
如果我省略 const obj
并使用 new Proxy({}, ...)
创建我的对象,我会得到预期的结果——一个代理 one
和一个代理 two
,大概是因为目标它们之间不共享引用。那么:究竟是什么?根据我的理解,使用 receiver
存储 name
应该可以防止它传播到目标对象,但它似乎还是这样做了。
直接在代理上设置属性时似乎会发生这种情况。该行为与实例化多个代理无关;创建单个代理并设置其 name
也会污染目标。
如相关问题 this answer 中所述,使用其原型设置为代理的继承对象不会污染代理的目标。
您的代码段
Object.defineProperty(receiver, prop, {
value: val,
configurable: true,
enumerable: true}
);
不会做(我认为)您期望它做的事情。由于这里的receiver
是代理对象,所以属性的定义也会被代理到target
,也就是说你的if/else中的分支之间的区别几乎没有。如果您希望为每个代理对象存储一个唯一的名称,那么在这种情况下最简单的做法就是使用闭包的作用域,例如
const proxies = ['one', 'two'].map(name => {
console.log(`proxy ${name}`);
const proxy = new Proxy(obj, {
get: (target, prop, receiver) => {
if (prop === 'name') { return name; }
return Reflect.get(target, prop, receiver);
},
set: (target, prop, val, receiver) => {
if (prop === 'name') {
name = val;
return true;
}
return Reflect.set(target, prop, val, receiver);
},
ownKeys: (target) => {
return Reflect.ownKeys(target).concat('name');
},
getOwnPropertyDescriptor: (target, prop) => {
if (prop === "name") return { enumerable: true, writable: true, configurable: true, value: name };
return Reflect.getOwnPropertyDescriptor(target, prop);
},
});
return proxy;
});
我正在尝试为 JavaScript 中的同一个目标对象创建多个代理包装器,每个包装器的属性略有不同,这些属性会影响包装功能的运行方式。这些属性被分配给 receiver
对象并从 set
和 get
处理程序中访问。但是,当我检查生成的代理时,它们都具有 属性 集,我希望它们已分配给创建的 last 代理。
const obj = {};
const proxies = ['one', 'two'].map(name => {
console.log(`proxy ${name}`);
const proxy = new Proxy(obj, {
get: (target, prop, receiver) => {
if (prop === 'name') { return receiver.name; }
return target[prop];
},
set: (target, prop, val, receiver) => {
if (prop === 'name') {
console.log(`setting name ${val} on receiver`);
Object.defineProperty(receiver, prop, {
value: val,
configurable: true,
enumerable: true}
);
} else {
console.log(`setting ${prop} ${val} on target`);
target[prop] = val;
}
return true;
}
});
proxy.name = name;
return proxy;
});
console.log();
console.log(proxies);
我的预期结果:[{name: 'one'}, {name: 'two'}]
.
实际结果:[{name: 'two'}, {name: 'two'}]
。尽管它们看起来相同,但它们并非严格相等。
如果我省略 const obj
并使用 new Proxy({}, ...)
创建我的对象,我会得到预期的结果——一个代理 one
和一个代理 two
,大概是因为目标它们之间不共享引用。那么:究竟是什么?根据我的理解,使用 receiver
存储 name
应该可以防止它传播到目标对象,但它似乎还是这样做了。
直接在代理上设置属性时似乎会发生这种情况。该行为与实例化多个代理无关;创建单个代理并设置其 name
也会污染目标。
如相关问题 this answer 中所述,使用其原型设置为代理的继承对象不会污染代理的目标。
您的代码段
Object.defineProperty(receiver, prop, {
value: val,
configurable: true,
enumerable: true}
);
不会做(我认为)您期望它做的事情。由于这里的receiver
是代理对象,所以属性的定义也会被代理到target
,也就是说你的if/else中的分支之间的区别几乎没有。如果您希望为每个代理对象存储一个唯一的名称,那么在这种情况下最简单的做法就是使用闭包的作用域,例如
const proxies = ['one', 'two'].map(name => {
console.log(`proxy ${name}`);
const proxy = new Proxy(obj, {
get: (target, prop, receiver) => {
if (prop === 'name') { return name; }
return Reflect.get(target, prop, receiver);
},
set: (target, prop, val, receiver) => {
if (prop === 'name') {
name = val;
return true;
}
return Reflect.set(target, prop, val, receiver);
},
ownKeys: (target) => {
return Reflect.ownKeys(target).concat('name');
},
getOwnPropertyDescriptor: (target, prop) => {
if (prop === "name") return { enumerable: true, writable: true, configurable: true, value: name };
return Reflect.getOwnPropertyDescriptor(target, prop);
},
});
return proxy;
});