How can I create a deep/recursive Proxy?
Specifically, I want to know whenever a property is set or modified anywhere in the object tree.
Here\'s what I\'ve got
Fixed a bunch of bugs in my original question. I think this works now:
function createDeepProxy(target, handler) {
const preproxy = new WeakMap();
function makeHandler(path) {
return {
set(target, key, value, receiver) {
if (typeof value === 'object') {
value = proxify(value, [...path, key]);
}
target[key] = value;
if (handler.set) {
handler.set(target, [...path, key], value, receiver);
}
return true;
},
deleteProperty(target, key) {
if (Reflect.has(target, key)) {
unproxy(target, key);
let deleted = Reflect.deleteProperty(target, key);
if (deleted && handler.deleteProperty) {
handler.deleteProperty(target, [...path, key]);
}
return deleted;
}
return false;
}
}
}
function unproxy(obj, key) {
if (preproxy.has(obj[key])) {
// console.log('unproxy',key);
obj[key] = preproxy.get(obj[key]);
preproxy.delete(obj[key]);
}
for (let k of Object.keys(obj[key])) {
if (typeof obj[key][k] === 'object') {
unproxy(obj[key], k);
}
}
}
function proxify(obj, path) {
for (let key of Object.keys(obj)) {
if (typeof obj[key] === 'object') {
obj[key] = proxify(obj[key], [...path, key]);
}
}
let p = new Proxy(obj, makeHandler(path));
preproxy.set(p, obj);
return p;
}
return proxify(target, []);
}
let obj = {
foo: 'baz',
}
let proxied = createDeepProxy(obj, {
set(target, path, value, receiver) {
console.log('set', path.join('.'), '=', JSON.stringify(value));
},
deleteProperty(target, path) {
console.log('delete', path.join('.'));
}
});
proxied.foo = 'bar';
proxied.deep = {}
proxied.deep.blue = 'sea';
delete proxied.foo;
delete proxied.deep; // triggers delete on 'deep' but not 'deep.blue'
You can assign full objects to properties and they'll get recursively proxified, and then when you delete them out of the proxied object they'll get deproxied so that you don't get notifications for objects that are no longer part of the object-graph.
I have no idea what'll happen if you create a circular linking. I don't recommend it.