javascript - How to watch for array changes? -


in javascript, there way notified when array modified using push, pop, shift or index-based assignment? want fire event handle.

i know watch() functionality in spidermonkey, works when entire variable set else.

there few options...

1. override push method

going quick , dirty route, override push() method array1:

object.defineproperty(myarray, "push", {   configurable: false,   enumerable: false, // hide for...in   writable: false,   value: function () {     (var = 0, n = this.length, l = arguments.length; < l; i++, n++) {                 raisemyevent(this, n, this[n] = arguments[i]); // assign/raise event     }     return n;   } }); 

1 alternatively, override array.prototype.push()... though, messing array.prototype ill-advised. still, if you'd rather that, replace myarray array.prototype.

now, that's 1 method , there lots of ways change array content. need more comprehensive...

2. create custom observable array

rather overriding methods, create own observable array. particular implementation copies array new array-like object , provides custom push(), pop(), shift(), unshift(), slice(), , splice() methods as as custom index accessors (provided array size modified via 1 of aforementioned methods or length property).

function observablearray(items) {    var _self = this,      _array = [],      _handlers = {        itemadded: [],        itemremoved: [],        itemset: []      };      function defineindexproperty(index) {      if (!(index in _self)) {        object.defineproperty(_self, index, {          configurable: true,          enumerable: true,          get: function() {            return _array[index];          },          set: function(v) {            _array[index] = v;            raiseevent({              type: "itemset",              index: index,              item: v            });          }        });      }    }      function raiseevent(event) {      _handlers[event.type].foreach(function(h) {        h.call(_self, event);      });    }      object.defineproperty(_self, "addeventlistener", {      configurable: false,      enumerable: false,      writable: false,      value: function(eventname, handler) {        eventname = ("" + eventname).tolowercase();        if (!(eventname in _handlers)) throw new error("invalid event name.");        if (typeof handler !== "function") throw new error("invalid handler.");        _handlers[eventname].push(handler);      }    });      object.defineproperty(_self, "removeeventlistener", {      configurable: false,      enumerable: false,      writable: false,      value: function(eventname, handler) {        eventname = ("" + eventname).tolowercase();        if (!(eventname in _handlers)) throw new error("invalid event name.");        if (typeof handler !== "function") throw new error("invalid handler.");        var h = _handlers[eventname];        var ln = h.length;        while (--ln >= 0) {          if (h[ln] === handler) {            h.splice(ln, 1);          }        }      }    });      object.defineproperty(_self, "push", {      configurable: false,      enumerable: false,      writable: false,      value: function() {        var index;        (var = 0, ln = arguments.length; < ln; i++) {          index = _array.length;          _array.push(arguments[i]);          defineindexproperty(index);          raiseevent({            type: "itemadded",            index: index,            item: arguments[i]          });        }        return _array.length;      }    });      object.defineproperty(_self, "pop", {      configurable: false,      enumerable: false,      writable: false,      value: function() {        if (_array.length > -1) {          var index = _array.length - 1,            item = _array.pop();          delete _self[index];          raiseevent({            type: "itemremoved",            index: index,            item: item          });          return item;        }      }    });      object.defineproperty(_self, "unshift", {      configurable: false,      enumerable: false,      writable: false,      value: function() {        (var = 0, ln = arguments.length; < ln; i++) {          _array.splice(i, 0, arguments[i]);          defineindexproperty(_array.length - 1);          raiseevent({            type: "itemadded",            index: i,            item: arguments[i]          });        }        (; < _array.length; i++) {          raiseevent({            type: "itemset",            index: i,            item: _array[i]          });        }        return _array.length;      }    });      object.defineproperty(_self, "shift", {      configurable: false,      enumerable: false,      writable: false,      value: function() {        if (_array.length > -1) {          var item = _array.shift();          delete _self[_array.length];          raiseevent({            type: "itemremoved",            index: 0,            item: item          });          return item;        }      }    });      object.defineproperty(_self, "splice", {      configurable: false,      enumerable: false,      writable: false,      value: function(index, howmany /*, element1, element2, ... */ ) {        var removed = [],            item,            pos;          index = index == null ? 0 : index < 0 ? _array.length + index : index;          howmany = howmany == null ? _array.length - index : howmany > 0 ? howmany : 0;          while (howmany--) {          item = _array.splice(index, 1)[0];          removed.push(item);          delete _self[_array.length];          raiseevent({            type: "itemremoved",            index: index + removed.length - 1,            item: item          });        }          (var = 2, ln = arguments.length; < ln; i++) {          _array.splice(index, 0, arguments[i]);          defineindexproperty(_array.length - 1);          raiseevent({            type: "itemadded",            index: index,            item: arguments[i]          });          index++;        }          return removed;      }    });      object.defineproperty(_self, "length", {      configurable: false,      enumerable: false,      get: function() {        return _array.length;      },      set: function(value) {        var n = number(value);        var length = _array.length;        if (n % 1 === 0 && n >= 0) {                  if (n < length) {            _self.splice(n);          } else if (n > length) {            _self.push.apply(_self, new array(n - length));          }        } else {          throw new rangeerror("invalid array length");        }        _array.length = n;        return value;      }    });      object.getownpropertynames(array.prototype).foreach(function(name) {      if (!(name in _self)) {        object.defineproperty(_self, name, {          configurable: false,          enumerable: false,          writable: false,          value: array.prototype[name]        });      }    });      if (items instanceof array) {      _self.push.apply(_self, items);    }  }    (function testing() {      var x = new observablearray(["a", "b", "c", "d"]);      console.log("original array: %o", x.slice());      x.addeventlistener("itemadded", function(e) {      console.log("added %o @ index %d.", e.item, e.index);    });      x.addeventlistener("itemset", function(e) {      console.log("set index %d %o.", e.index, e.item);    });      x.addeventlistener("itemremoved", function(e) {      console.log("removed %o @ index %d.", e.item, e.index);    });       console.log("popping , unshifting...");    x.unshift(x.pop());      console.log("updated array: %o", x.slice());      console.log("reversing array...");    console.log("updated array: %o", x.reverse().slice());      console.log("splicing...");    x.splice(1, 2, "x");    console.log("setting index 2...");    x[2] = "foo";      console.log("setting length 10...");    x.length = 10;    console.log("updated array: %o", x.slice());      console.log("setting length 2...");    x.length = 2;      console.log("extracting first element via shift()");    x.shift();      console.log("updated array: %o", x.slice());    })();

see object.defineproperty() reference.

that gets closer it's still not bullet proof... brings to:

3. proxies

in future1, proxies may offer solution... allowing intercept method calls, accessors, etc. importantly, can without providing explicit property name... allow test arbitrary, index-based access/assignment. can intercept property deletion. proxies allow inspect change before deciding allow it... in addition handling change after fact.

here's stripped down sample:

(function() {      if (!("proxy" in window)) {      console.warn("your browser doesn't support proxies.");      return;    }      // our backing array    var array = ["a", "b", "c", "d"];      // proxy our array    var proxy = new proxy(array, {      apply: function(target, thisarg, argumentslist) {        return thisarg[target].apply(this, argumentlist);      },      deleteproperty: function(target, property) {        console.log("deleted %s", property);        return true;      },      set: function(target, property, value, receiver) {              target[property] = value;        console.log("set %s %o", property, value);        return true;      }    });      console.log("set specific index..");    proxy[0] = "x";      console.log("add via push()...");    proxy.push("z");      console.log("add/remove via splice()...");    proxy.splice(1, 3, "y");      console.log("current state of array: %o", array);    })();

1 browser support getting better there still noticeable holes.


Comments

Popular posts from this blog

linux - Mailx and Gmail nss config dir -

c# - Is it possible to remove an existing registration from Autofac container builder? -

php - Mysql PK and FK char(36) vs int(10) -