Home > Software design >  Run a function when deep property is set
Run a function when deep property is set

Time:05-18

I have an object like

const obj = { field1: obj1, field2: obj2 }

and now I'd like to run a function when anything in obj was changed:

function objChanged() { ... }

// decorate obj somehow ...

obj.field3 = data; // objChanged should be called (Proxy can see it)
obj.field1.val = data; //objChanged should be called (Proxy can't see it?)

AFAIK there is a MutationObserver which works only for DOM and Proxy which intercepts only own properties, right?

I do not own obj1 so I can not change it. Is there a way to achieve this functionality?

CodePudding user response:

Following the piece of code will listen to object property you can iterate over object properties to listen all. I am curious, what are you trying to achieve?

const dog = { bark: true };

function Observer(o, property) {
  var _this = this;
  this.observers = [];

  this.Observe = function (notifyCallback) {
    _this.observers.push(notifyCallback);
  };

  Object.defineProperty(o, property, {
    set: function (val) {
      _this.value = val;
      for (var i = 0; i < _this.observers.length; i  ) {
        _this.observers[i](val);
      }
    },
    get: function () {
      return _this.value;
    },
  });
}

const observer = new Observer(dog, "bark");

observer.Observe(function (value) {
  l("Barked");
});

dog.bark = true;
dog.bark = true;
dog.bark = true;
dog.bark = true;

CodePudding user response:

Orgil's answer works only with a single property that needs to be known and encoded. I wanted a solution which works for all properties, including later added. Inspired by his idea to create an observing object, I created a dynamic Proxy that adds another Proxies when needed.

In the following code dog1 serves as proxy: setting its properties modifies the original dog object and logs the assigned value to console.

function AssignProxy(o, fn) {
    var tree = {};
    return new Proxy(o, {
        get: (_, prop) => {
            if(typeof o[prop] != "object") return o[prop];
            if(tree[prop] === undefined) tree[prop] = AssignProxy(o[prop], fn);
            return tree[prop];
        },
        set: (_, prop, val) => {
            fn(o[prop] = val);
            return true;
        }
    });
}


/****** TEST *******/

const dog = {
    sounds: {},
    name: "Spike"
};

const dog1 = AssignProxy(dog, val => console.log("assigning " val));

dog1.name = "Tyke"; // overwrite property
dog1.age = 4; // create a property
dog1.sounds.howl = "hoooooowl"; // create a deep property
dog1.sounds.howl = {text: "hoowl", pitch: 5000}; // overwrite the deep property
var howl = dog1.sounds.howl; // access by reference
howl.pitch = 6000; // overwrite later added property
console.log(dog); // verify the original object

  • Related