问题
I can take a Javascript object o
and create a new Proxy object from it:
let p = new Proxy(object, { ... })
But is there a way to mutate an existing object reference to track changes on the original object? In particular, is there a way I can track the addition of new keys on the object from exterior sources?
回答1:
The Proxy spec supports defining a proxy on the prototype of an object as a means for inspecting actions on that object when they do not exist on the instance. While this isn't full parity with .watch()
it does allow for your mentioned use case of knowing when new properties are added. Here is an example, with comments about caveats...
// assuming some existing property you didn't create...
const t = { existing: true };
// proxy a new prototype for that object...
const ctr = {};
Object.setPrototypeOf(t, new Proxy(ctr, {
get(target, key) {
console.log('icu get');
return Reflect.get(target, key) || ctr[key];
},
set(target, key, val) {
console.log('icu set');
// setting this container object instead of t keeps t clean,
// and allows get access to that property to continue being
// intercepted by the proxy
Reflect.set(ctr, key, val);
return true;
},
deleteProperty(target, key) {
console.log('icu delete');
delete ctr[key];
return true;
}
}));
// existing properties don't work
console.log('existing');
t.existing; // <nothing>
t.existing = false; // <nothing>
// new properties work
console.log('new');
t.test; // icu get
t.test = 4; // icu set
console.log(t.test); // icu get
// 4
// but this doesn't work (and I think it should be a bug)
console.log('delete');
delete t.test; // icu get
// <missing icu delete>
console.log(t.test); // 4
回答2:
Just create the object first and keep a reference to it before creating its Proxy.
Now you can modify either of them (the original object or its Proxy) and the other will also receive the changes unless you prevent them on the Proxy:
const o = {};
const p = new Proxy(o, {
set: function(obj, prop, value) {
if (prop === 'd') {
return false;
}
obj[prop] = value;
return true;
},
});
// These operations are forwarded to the target object o:
p.a = 0;
p.b = 1;
// This one is prevented by the Proxy:
p.d = true;
// Both will have two properties, a and b:
console.log(o);
// You can also mutate the original object o and the Proxy will also get those changes:
o.c = false;
// Note that now the Proxy setter is not called, so you can do:
o.d = true;
// But the Proxy still gets the change:
console.log(p);
If you want to be notified when a new property is added, deleted or modified on an object without the possiblity that the original reference is used to mutate the original object directly, the only option you have is to create that object directly as a Proxy or overwrite the original one:
// Created from an empty object without a reference to it:
// const p = new Proxy({}, { ... });
// Overwrite the original reference:
let myObject = { a: 1, b: 2 };
myObject = new Proxy(myObject, {
set: function(obj, prop, value) {
if (prop in obj) {
console.log(`Property ${ prop } updated: ${ value }`);
} else {
console.log(`Property ${ prop } created: ${ value }`);
}
obj[prop] = value;
return true;
},
deleteProperty(obj, prop) {
console.log(`Property ${ prop } deleted`);
delete obj[prop];
}
});
// Now there's no way to access the original object we
// passed in as the Proxy's target!
myObject.a = true;
myObject.a = false;
delete myObject.a;
There used to be an Object.prototype.watch(), but it has been deprecated.
来源:https://stackoverflow.com/questions/52031628/transform-a-javascript-object-into-a-proxy-and-not-its-reference