admin管理员组

文章数量:1434943

This may seem silly, but in this day and age, one should be able to expect JS to raise an event if contents of an array have changed.

A few questions been asked regarding getting notified when a variable changes (define getter or setter). And there seems to be a way to do that (at least for most browsers including IE6+)

My issue is that I'm trying to get notified if an item inside an array changes:

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

Obviously, I'm trying to avoid forcing the coder to use old-school Java style .getVale() and .setValue() functions to access/modify data.

This may seem silly, but in this day and age, one should be able to expect JS to raise an event if contents of an array have changed.

A few questions been asked regarding getting notified when a variable changes (define getter or setter). And there seems to be a way to do that (at least for most browsers including IE6+)

My issue is that I'm trying to get notified if an item inside an array changes:

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

Obviously, I'm trying to avoid forcing the coder to use old-school Java style .getVale() and .setValue() functions to access/modify data.

Share Improve this question edited Sep 22, 2019 at 8:58 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Jun 6, 2012 at 15:40 MandoMandoMandoMando 5,5154 gold badges30 silver badges36 bronze badges 6
  • Backbone.js can trigger events when collection models change. Not quite the same thing, but you could see how they do it. – asawyer Commented Jun 6, 2012 at 15:42
  • 1 You could have the array as a private variable of an object, then the only way to change the variable is use a method on that object, which can fire an event... – Abe Petrillo Commented Jun 6, 2012 at 15:45
  • 1 I think you should read about the observable pattern – MilkyWayJoe Commented Jun 6, 2012 at 15:47
  • 2 Possible duplicate of stackoverflow./questions/2449182/… Looks like you can use defineGetter/defineSetter on an array but only for fixed keys. You can't define a property for “all names that happen to be integers” all in one go. – Dmitry Pashkevich Commented Jun 6, 2012 at 15:47
  • @asawyer, Abe and MilkyWayJoe: great suggestions, but they all require mod to existing syntax array[indexz] = x; Dmitry: don't think duplicate, but a great post by bobince. This means that JS would need a onProperyAdded hook to then add a custom getter setter to the array items dynamically. If one of you would like to post that as the answer, think we're there. – MandoMando Commented Jun 6, 2012 at 16:05
 |  Show 1 more ment

3 Answers 3

Reset to default 3

In short: no, you can't. You'll notice that Arrays don't provide any event dispatching mechanism, and their API doesn't include any callback type functionality.

In longer: as others have noted, it is possible to wrap the array… And it's also possible to poll the arrays contents:

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

But this method has some obvious problems: it polls, it'll get slow to watch a bunch of arrays, etc.

I think timeout-based solutions are not the best.
If you can only use push and pop to modify your array, you can override push and pop methods of Array prototype (or only some object that you want to monitor):

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

If push and pop are not sufficient, you can add some setAt method to use like myWatchableArr.setAt(3, value) instead of myWatchableArr[3]=value.

Ok, based on @David Wolever's code and other ments, there actually is a solution:

Use the notes from John Dyer to implement the addProperty method. Place a setTimeout in the getter method to pare with original value a short time after the read takes place:

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

本文标签: javascriptIs there a way to get a callback when an array item value has changedStack Overflow