如何看arrays变化?
在Javascript中,有没有一种方法可以通过推,popup,移位或基于索引的赋值来修改数组? 我想要一些可以解决事件的东西。
我知道SpiderMonkey中的watch()函数,但是只有在整个variables被设置为其他的时候才有效。
有几个选项…
1.覆盖推送方法
走快速和肮脏的路线,你可以覆盖你的数组1的push()
方法:
Object.defineProperty(myArray, "push", { configurable: false, enumerable: false, // hide from for...in writable: false, value: function () { for (var i = 0, n = this.length, l = arguments.length; i < l; i++, n++) { RaiseMyEvent(this, n, this[n] = arguments[i]); // assign/raise your event } return n; } });
1或者,您可以重写Array.prototype.push()
…但是,与Array.prototype
通常是不明智的。 不过,如果你想这样做,只需用Array.prototype
replacemyArray
。
现在,这只是一种方法,有很多方法来改变数组内容。 我们可能需要更全面的东西…
2.创build一个自定义的可观察数组
而不是重写方法,你可以创build自己的可观察数组。 这个特定的实现将一个数组复制到一个新的类数组对象中,并提供自定义的push()
, pop()
, shift()
, unshift()
, slice()
和splice()
方法以及自定义索引访问器数组大小只能通过上述方法之一或length
属性进行修改)。
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; for (var i = 0, ln = arguments.length; i < 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() { for (var i = 0, ln = arguments.length; i < ln; i++) { _array.splice(i, 0, arguments[i]); defineIndexProperty(_array.length - 1); raiseEvent({ type: "itemadded", index: i, item: arguments[i] }); } for (; 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 }); } for (var i = 2, ln = arguments.length; i < 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 at index %d.", e.item, e.index); }); x.addEventListener("itemset", function(e) { console.log("Set index %d to %o.", e.index, e.item); }); x.addEventListener("itemremoved", function(e) { console.log("Removed %o at index %d.", e.item, e.index); }); console.log("popping and 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 to 10..."); x.length = 10; console.log("updated array: %o", x.slice()); console.log("setting length to 2..."); x.length = 2; console.log("extracting first element via shift()"); x.shift(); console.log("updated array: %o", x.slice()); })();
我发现以下似乎完成这个: https : //github.com/mennovanslooten/Observable-Arrays
Observable-Arrays扩展了下划线,可以使用如下:(从该页面)
// For example, take any array: var a = ['zero', 'one', 'two', 'trhee']; // Add a generic observer function to that array: _.observe(a, function() { alert('something happened'); });
从阅读所有的答案,我已经组装了一个简化的解决scheme,不需要任何外部库。
这也说明了这种方法的总体思路:
function processQ() { // ... this will be called on each .push } var myEventsQ = []; myEventsQ.push = function() { Array.prototype.push.apply(this, arguments); processQ();};
if (!Array.prototype.forEach) { Object.defineProperty(Array.prototype, 'forEach', { enumerable: false, value: function(callback) { for(var index = 0; index != this.length; index++) { callback(this[index], index, this); } } }); } if(Object.observe) { Object.defineProperty(Array.prototype, 'Observe', { set: function(callback) { Object.observe(this, function(changes) { changes.forEach(function(change) { if(change.type == 'update') { callback(); } }); }); } }); } else { Object.defineProperties(Array.prototype, { onchange: { enumerable: false, writable: true, value: function() { } }, Observe: { set: function(callback) { Object.defineProperty(this, 'onchange', { enumerable: false, writable: true, value: callback }); } } }); var names = ['push', 'pop', 'reverse', 'shift', 'unshift']; names.forEach(function(name) { if(!(name in Array.prototype)) { return; } var pointer = Array.prototype[name]; Array.prototype[name] = function() { pointer.apply(this, arguments); this.onchange(); } }); } var a = [1, 2, 3]; a.Observe = function() { console.log("Array changed!"); }; a.push(8);
一个有趣的collections库是https://github.com/mgesmundo/smart-collection 。 允许您观看数组并为其添加视图。 不确定的performance,因为我正在testing自己。 将很快更新这个职位。
我不build议你扩展原生的原型。 相反,你可以使用像new-list这样的库; https://github.com/azer/new-list
它创build一个原生的JavaScript数组,并允许您订阅任何更改。 它批量更新,并给你最后的差异;
List = require('new-list') todo = List('Buy milk', 'Take shower') todo.pop() todo.push('Cook Dinner') todo.splice(0, 1, 'Buy Milk And Bread') todo.subscribe(function(update){ // or todo.subscribe.once update.add // => { 0: 'Buy Milk And Bread', 1: 'Cook Dinner' } update.remove // => [0, 1] })