Skip to main content
edited title
Link

Javascript: log Listener for property value changes onin a Javascript object property

Source Link

Javascript: log changes on object property

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?