javascript - How can I trigger a callback when an object attribute is modified? - Stack Overflow

If I have a class like:class Myclass {constructor(){this.a = [];this.sum = 0;}update(){this.sum = this.

If I have a class like:

class Myclass {
    constructor(){
        this.a = [];
        this.sum = 0;
    }

    update(){
       this.sum = this.a.reduce((a, b) => a + b, 0)
    }
}

I instantiate the class:

myClass = new Myclass();

I append a number to the a attribute

myClass.a.push(1);
myClass.a.push(2);

How can I call the update method every time the a attribute is modified? In other words, how can I trigger a callback when an object attribute is altered?

If I have a class like:

class Myclass {
    constructor(){
        this.a = [];
        this.sum = 0;
    }

    update(){
       this.sum = this.a.reduce((a, b) => a + b, 0)
    }
}

I instantiate the class:

myClass = new Myclass();

I append a number to the a attribute

myClass.a.push(1);
myClass.a.push(2);

How can I call the update method every time the a attribute is modified? In other words, how can I trigger a callback when an object attribute is altered?

Share Improve this question asked Apr 27, 2020 at 12:20 NeilNeil 7969 silver badges28 bronze badges 1
  • 1 In general, you could use a getter/setter for this, but the .a attribute is not altered, there is no assignment to it. Rather, the array that the property holds is mutated, and detecting that is a bit more plicated. – Bergi Commented Apr 27, 2020 at 12:23
Add a ment  | 

4 Answers 4

Reset to default 7

Add method for pushing new items to the a array, e.g:

class Myclass {
    constructor(){
        this.a = [];
        this.sum = 0;
    }

    update(){
       this.sum = this.a.reduce((a, b) => a + b, 0)
    }

    pushItem(val) {
        this.a.push(val);
        this.update();
    }

}

myClass = new Myclass();
myClass.pushItem(1);
myClass.pushItem(2);
console.log(myClass.sum);

One way is to extend Array and shadow the push method.

class MyArray extends Array{
  constructor(ref){
    super();
    this.ref = ref
  }
  push(value){
    super.push(value)
    this.ref.update()
    console.log("called")
  }
}

class Myclass {
    constructor(){
        this.a = new MyArray(this);
        this.sum = 0;
    }
   
    update(){
       this.sum = this.a.reduce((a, b) => a + b, 0)
    }
}

let myClass = new Myclass()
myClass.a.push(1);
myClass.a.push(2);

console.log(myClass.sum)

P.S:- Certainly not a good idea to call the callback on each insertion if you're not using the sum value after insertion.

In other words, how can I trigger a callback when an object attribute is altered?

This is a general solution to the above question. If you need a use-case specific solution for the a array only, please ignore this.

Check ES6 Proxies for something more powerful than simple getters/setters for get/set interception. You may want to recursively wrap with Proxies since you have a nested data structure. Something like this:

function wrapWithProxyCallback(obj, callback) {
  return new Proxy(obj, {
    get (target, key, receiver) {
      const result = Reflect.get(target, key, receiver)
      // wraps nested data with Proxy
      if (typeof result === 'object' && result !== null) {
        return wrapWithProxyCallback(result, callback)
      }
      return result
    },
    set (target, key, value, receiver) {
      // calls callback on every set operation
      callback(target, key, value /* and whatever else you need */)
      return Reflect.set(target, key, value, receiver)
    }
  })
}

And use it like:

const wrappedClass = wrapWithCallback(
  myClass,
  (target, key) => console.log(target[key])
)

// this will call the above callback
wrappedClass.a.push(1);

This is a bit naive but a good general starting point. You may want to check if nested data is already wrapped by Proxies for example.

What you want to do is probably nicely aligning with transparent reactivity (I would have to see your callback's content to be sure about this though.) I think you should check out mobx or @nx-js/observer-util (the latter is authored by me) for production-ready boxed solutions.

Edit: My first example triggered the callback on get operations, I corrected that to trigger on set operations.

Maybe by organizing the object as

class Myclass {
    constructor(){
        this.a = [];
    }

    get sum(){
       return this.a.reduce((a, b) => a + b, 0)
    }
}

If I'm not mistaken calling myClass.sum will return the total of values in the array held under the a attribute.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744726411a4590207.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信