【问题标题】:How can I trigger a callback when an object attribute is modified?修改对象属性时如何触发回调?
【发布时间】:2020-04-27 12:20:33
【问题描述】:

如果我有这样的课程:

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

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

我实例化了这个类:

myClass = new Myclass();

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

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

如何在每次修改a属性时调用update方法?也就是说,当对象属性发生变化时,如何触发回调?

【问题讨论】:

  • 一般而言,您可以为此使用 getter/setter,但 .a 属性 更改,没有分配给它。相反,该属性持有的数组发生了变异,检测起来有点复杂。

标签: javascript class ecmascript-6 callback


【解决方案1】:

添加将新项目推送到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);

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      也就是说,当对象属性发生变化时,如何触发回调?

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

      检查ES6 Proxies 是否有比简单的getter/setter 更强大的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 操作。

      【讨论】:

        【解决方案4】:

        也许通过将对象组织为

        class Myclass {
            constructor(){
                this.a = [];
            }
        
            get sum(){
               return this.a.reduce((a, b) => a + b, 0)
            }
        }
        

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

        【讨论】:

          猜你喜欢
          • 2019-05-23
          • 1970-01-01
          • 1970-01-01
          • 2018-07-21
          • 1970-01-01
          • 2014-06-14
          • 2021-05-15
          • 2017-05-07
          • 1970-01-01
          相关资源
          最近更新 更多