【问题标题】:Is there a way to get a callback when an array item value has changed?有没有办法在数组项值发生更改时获取回调?
【发布时间】:2012-06-06 15:40:23
【问题描述】:

这可能看起来很愚蠢,但在当今时代,如果数组的内容发生了变化,人们应该能够期望 JS 引发事件。

关于在变量更改(定义 getter 或 setter)时获得通知的几个问题。而且似乎有a way to do that(至少对于大多数浏览器,包括IE6+)

我的问题是,如果数组中的项目发生更改,我会尝试收到通知:

    var ar = ["one", "two", "three"];
    // setting the whole array will call the custom setter method
    // (assuming you defined it)

    ar = ["one", "three", "five"];

    // however, this will only call the getter method and won't call the setter
    // without defining custom setters for every item in the array.  

    ar[1] = "two";

显然,我试图避免强迫编码人员使用老式 Java 风格的 .getVale().setValue() 函数来访问/修改数据。

【问题讨论】:

  • Backbone.js 可以在集合模型发生变化时触发事件。不完全相同,但你可以看到他们是如何做到的。
  • 您可以将数组作为对象的私有变量,然后更改变量的唯一方法是在该对象上使用可以触发事件的方法...
  • 我认为你应该阅读observable pattern
  • stackoverflow.com/questions/2449182/… 的可能重复项看起来您可以在数组上使用defineGetter/defineSetter,但只能用于固定键。 You can't define a property for “all names that happen to be integers” all in one go.
  • @asawyer、Abe 和 MilkyWayJoe:很好的建议,但它们都需要修改现有语法 array[indexz] = x;德米特里:不要认为重复,但 bobince 的一篇很棒的帖子。这意味着 JS 需要一个 onProperyAdded 钩子,然后动态地将自定义 getter setter 添加到数组项中。如果你们中的某个人想将其发布为答案,请认为我们在那里。

标签: javascript cross-browser dom-events


【解决方案1】:

简而言之:不,你不能。您会注意到Arrays 不提供任何事件分派机制,并且它们的 API 不包含任何回调类型的功能。

在更长的时间内:正如其他人所指出的,可以包装数组......而且还可以轮询数组内容:

function watchArray(arr, callback) {
    var oldVal = "" + arr;
    setInterval(function() {
        var curVal = "" + arr;
        if (curVal != oldVal) {
            callback();
            oldVal = curVal;
        }
    }, 100);
}

但是这种方法有一些明显的问题:它会轮询,观察一堆数组会变慢等等。

【讨论】:

  • 感谢这篇文章。因为当数组元素改变时getter会被调用,而你的方法是比较=> getter + delay + comapre = change notification。缓慢的问题是找出哪个元素发生了变化(对我来说不是问题)。
  • 上面@Phrogz 建议的新defineProperty 方法怎么样?
【解决方案2】:

好的,根据@David Wolever的代码和其他cmets,其实是有解决办法的:

使用John Dyer 中的注释来实现 addProperty 方法。在 getter 方法中放置一个 setTimeout 以在读取发生后不久与原始值进行比较:

addProperty(myObject, 'vals',
    function () {
        var _oldVal = "" + this._val;
        var _parent = this;
        console.log('getter!');
        setTimeout(function () {
            var curVal = "" + _parent._val;
            if (curVal != _oldVal)
                console.log('array changed!');
        }, 200);
        return this._val;
    },
    function (value) {
        console.log('setter!');
        this._val = value;
    });

    myObject.vals = ["one", "two", "three"];
    myObject.vals[1] = "five";

【讨论】:

    【解决方案3】:

    我认为基于timeout 的解决方案不是最好的。
    如果您只能使用 push 和 pop 来修改您的数组,您可以覆盖 Array 原型的 pushpop 方法(或仅您要监视的某些对象):

    var myWatchableArr = [];
    myWatchableArr.setChangeCallback = function(callback){
        this.changeCallback = callback;
    }
    myWatchableArr.push = function(e){
        Array.prototype.push.call(this,e);
        if(typeof this.changeCallback == "function")
          this.changeCallback(this);
    }
    myWatchableArr.push(3);
    myWatchableArr.setChangeCallback(function(arr){
        console.log("the array has been changed", arr);
    });
    // now watching for changes
    myWatchableArr.push(4);
    

    如果 push 和 pop 不够用,你可以添加一些 setAt 方法来使用,例如 myWatchableArr.setAt(3, value) 而不是 myWatchableArr[3]=value

    【讨论】:

      猜你喜欢
      • 2013-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-22
      • 2020-10-24
      • 2014-12-07
      • 2019-07-12
      相关资源
      最近更新 更多