The purpose of this code is add proxy on nested object for log changes on property. This is a debug utility!
// check if value is object or array
function isObject(value) {
return value !== null && typeof value === "object" && ["Array", "Object"].includes(value.constructor.name);
}
// add nested proxy
function buildProxy(poj, callback = console.log, tree = []) {
const getPath = (prop) => tree.concat(prop).join(".");
return new Proxy(poj, {
get(target, prop) {
const value = Reflect.get(target, prop);
if (isObject(value)) {
return buildProxy(value, callback, tree.concat(prop));
}
return value;
},
set(target, prop, value) {
callback({
action: "set",
path: getPath(prop),
newValue: value,
preValue: Reflect.get(target, prop, value),
stack: new Error().stack.split("\n")[1].trim()
});
return Reflect.set(target, prop, value);
},
deleteProperty(target, prop) {
callback({ action: "delete", path: getPath(prop), stack: new Error().stack.split("\n")[1].trim() });
return Reflect.deleteProperty(target, prop);
},
});
}
// remove nested proxy
const unbuildProxy = (value) => {
if (isObject(value)) {
if (Array.isArray(value)) {
value = [...value];
} else {
value = { ...value };
}
for (const key in value) {
value[key] = unbuildProxy(value[key]);
}
}
return value;
}
// set nested proxy on data
let data = buildProxy(
{
name: "David",
occupation: "freelancer",
children: [{ name: "oliver" }, { name: "ruby" }],
nested: { a: { b: true } }
}
);
// test console.log on changes
data.name = "Mike";
data.children.push({ name: "baby" });
data.children[1].name = "fred";
delete data.occupation;
data.nested.a.b = { c: true };
data.nested.a.b.c = "Mike2";
// remove nested proxy
data = unbuildProxy(data)
// structuredClone raise exception if object is proxy
console.log(structuredClone(data))
there are improvements?