在Javascript中,当使用基于推,弹出,移位或基于索引的分配修改数组时,是否有通知方法?我想要能够触发我可以处理的事件的东西。
我知道watch()SpiderMonkey 的功能,但是仅当整个变量设置为其他值时,该功能才起作用。
watch()
有一些选择…
走快速而肮脏的路线,您可以覆盖push()数组1的方法:
push()
Object.defineProperty(myArray, "push", { enumerable: false, // hide from for...in configurable: false, // prevent further meddling... writable: false, // see above ^ 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()。但是要小心;您环境中的其他代码可能不喜欢或期望这种修改。不过,如果所有内容听起来都很吸引人,请替换myArray为Array.prototype。
Array.prototype.push()
myArray
Array.prototype
现在,这只是一种方法,并且有很多更改数组内容的方法。我们可能需要更全面的信息…
您可以创建自己的可观察数组,而不是覆盖方法。此特定实现拷贝的阵列到一个新的数组状物体并提供定制push(),pop(),shift(),unshift(),slice(),和splice()的方法 以及 定制索引访问器(条件是数组大小仅通过上述方法或一种修饰的length属性)。
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()); })();
请参阅以供参考。Object.[defineProperty()](https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
Object.[defineProperty()](https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
这使我们更加接近,但仍不是防弹措施……这使我们能够:
代理提供了另一种解决方案…允许您拦截方法调用,访问器等。最重要的是,您甚至不需要提供明确的属性名称就可以执行此操作…这将允许您测试任意的,基于索引的访问/分配。您甚至可以拦截属性删除。代理可以有效地让您 在 决定允许更改 之前 检查更改…除了在事后处理更改之外。
这是精简的示例:
(function() { if (!("Proxy" in window)) { console.warn("Your browser doesn't support Proxies."); return; } // our backing array var array = ["a", "b", "c", "d"]; // a proxy for 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 to %o", property, value); return true; } }); console.log("Set a 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); })();