如何在修改对象属性时触发回调?

How can I trigger a callback when an object attribute is modified?

如果我class喜欢:

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

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

我实例化 class:

myClass = new Myclass();

我在 a 属性中附加了一个数字

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

如何在每次修改a属性时调用update方法?换句话说,如何在对象属性更改时触发回调?

也许通过将对象组织为

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

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

如果我没记错,调用 myClass.sum 将 return a 属性下保存的数组中的总值。

添加将新项目推送到 a 数组的方法,例如:

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);

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

这是对上述问题的一般解决方案。如果您只需要 a 数组的用例特定解决方案,请忽略此。

检查 ES6 Proxies 比简单的 getters/setters get/set 拦截更强大的东西。由于你有一个嵌套的数据结构,你可能想用代理递归包装。像这样:

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)
    }
  })
}

并像这样使用它:

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

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

这有点天真,但却是一个很好的通用起点。例如,您可能想检查嵌套数据是否已被代理包装。

你想做的可能是与透明反应性很好地保持一致(不过我必须查看你的回调内容才能确定这一点。)我认为你应该查看 mobx 或@nx-js/observer-util(后者由我编写)用于生产就绪的盒装解决方案。

编辑:我的第一个示例触发了 get 操作的回调,我更正了它以触发 set 操作。

一种方法是扩展 Array 并隐藏 push 方法。

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:- 如果您在插入后不使用总和值,那么在每次插入时调用回调当然不是一个好主意。