Javascript 如何监视阵列更改?
在Javascript中,是否有一种方法可以在使用push、pop、shift或基于索引的赋值修改数组时得到通知?我想要一些能引发我能处理的事件的东西Javascript 如何监视阵列更改?,javascript,dom-events,Javascript,Dom Events,在Javascript中,是否有一种方法可以在使用push、pop、shift或基于索引的赋值修改数组时得到通知?我想要一些能引发我能处理的事件的东西 我知道SpiderMonkey中的watch()功能,但只有当整个变量设置为其他变量时,它才起作用。有几个选项 1.覆盖推送方法 按照快速而肮脏的路线,您可以覆盖阵列1的“push()”方法: 我发现以下几点似乎可以做到这一点: 可观察数组扩展了下划线,可按如下方式使用: (摘自该页) 我不建议您扩展本机原型。相反,您可以使用类似于新列表的库
我知道SpiderMonkey中的
watch()
功能,但只有当整个变量设置为其他变量时,它才起作用。有几个选项
1.覆盖推送方法
按照快速而肮脏的路线,您可以覆盖阵列1的“push()”方法:
我发现以下几点似乎可以做到这一点: 可观察数组扩展了下划线,可按如下方式使用: (摘自该页)
我不建议您扩展本机原型。相反,您可以使用类似于新列表的库 它创建一个本地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]
})
这里有一个有趣的藏书室。允许您查看阵列并向其添加视图。不确定性能,因为我自己正在测试。很快就会更新这篇文章。通过阅读这里的所有答案,我组装了一个简化的解决方案,不需要任何外部库 它还更好地说明了该方法的总体思路:
function processQ() {
// ... this will be called on each .push
}
var myEventsQ = [];
myEventsQ.push = function() { Array.prototype.push.apply(this, arguments); processQ();};
我使用以下代码来侦听对数组的更改
/* @arr array you want to listen to
@callback function that will be called on any change inside array
*/
function listenChangesinArray(arr,callback){
// Add more methods here if you want to listen to them
['pop','push','reverse','shift','unshift','splice','sort'].forEach((m)=>{
arr[m] = function(){
var res = Array.prototype[m].apply(arr, arguments); // call normal behaviour
callback.apply(arr, arguments); // finally call the callback supplied
return res;
}
});
}
希望这是有用的:)不确定这是否涵盖了所有内容,但我使用类似的方法(特别是在调试时)来检测数组何时添加了元素:
var array = [1,2,3,4];
array = new Proxy(array, {
set: function(target, key, value) {
if (Number.isInteger(Number(key)) || key === 'length') {
debugger; //or other code
}
target[key] = value;
return true;
}
});
我胡思乱想,想出了这个。其思想是该对象定义了所有Array.prototype方法,但在单独的Array对象上执行它们。这使我们能够观察shift()、pop()等方法,尽管有些方法(如concat()等)不会返回OArray对象。如果使用访问器,重载这些方法将不会使对象可见。为了实现后者,将为给定容量内的每个索引定义访问器 性能方面。。。与普通阵列对象相比,OArray大约慢10-25倍。对于1-100范围内的能力,差异为1x-3x
class OArray {
constructor(capacity, observer) {
var Obj = {};
var Ref = []; // reference object to hold values and apply array methods
if (!observer) observer = function noop() {};
var propertyDescriptors = Object.getOwnPropertyDescriptors(Array.prototype);
Object.keys(propertyDescriptors).forEach(function(property) {
// the property will be binded to Obj, but applied on Ref!
var descriptor = propertyDescriptors[property];
var attributes = {
configurable: descriptor.configurable,
enumerable: descriptor.enumerable,
writable: descriptor.writable,
value: function() {
observer.call({});
return descriptor.value.apply(Ref, arguments);
}
};
// exception to length
if (property === 'length') {
delete attributes.value;
delete attributes.writable;
attributes.get = function() {
return Ref.length
};
attributes.set = function(length) {
Ref.length = length;
};
}
Object.defineProperty(Obj, property, attributes);
});
var indexerProperties = {};
for (var k = 0; k < capacity; k++) {
indexerProperties[k] = {
configurable: true,
get: (function() {
var _i = k;
return function() {
return Ref[_i];
}
})(),
set: (function() {
var _i = k;
return function(value) {
Ref[_i] = value;
observer.call({});
return true;
}
})()
};
}
Object.defineProperties(Obj, indexerProperties);
return Obj;
}
}
classoarray{
建造师(能力、观察员){
var Obj={};
var Ref=[];//引用对象以保存值并应用数组方法
如果(!observer)observer=函数noop(){};
var propertyDescriptors=Object.getOwnPropertyDescriptors(Array.prototype);
key(propertyDescriptors).forEach(函数(属性){
//该属性将绑定到Obj,但应用于Ref!
变量描述符=propertyDescriptors[属性];
变量属性={
可配置:descriptor.configurable,
可枚举:描述符。可枚举,
可写:descriptor.writable,
值:函数(){
观察员呼叫({});
返回描述符.value.apply(Ref,参数);
}
};
//长度例外
如果(属性=='length'){
删除属性.value;
删除属性。可写;
attributes.get=函数(){
返回参考长度
};
attributes.set=函数(长度){
参考长度=长度;
};
}
Object.defineProperty(对象、属性、属性);
});
变量索引器属性={};
对于(var k=0;k<容量;k++){
索引器属性[k]={
对,,
get:(函数(){
var_i=k;
返回函数(){
返回Ref[_i];
}
})(),
set:(函数(){
var_i=k;
返回函数(值){
Ref[_i]=值;
观察员呼叫({});
返回true;
}
})()
};
}
Object.defineProperties(Obj,索引器属性);
返回Obj;
}
}
最受欢迎的覆盖推送方法由@canon提供的解决方案有一些副作用,这些副作用对我来说是不方便的:
- 它使推送属性描述符不同(
和writable
应设置为configurable
,而不是true
),这会在以后导致异常false
- 当使用多个参数(例如
)调用一次myArray.push(“a”、“b”)
时,它会多次引发事件,在我的情况下,这是不必要的,对性能有害push()
Object.defineProperty(myArray, "push", {
configurable: true,
enumerable: false,
writable: true, // Previous values based on Object.getOwnPropertyDescriptor(Array.prototype, "push")
value: function (...args)
{
let result = Array.prototype.push.apply(this, args); // Original push() implementation based on https://github.com/vuejs/vue/blob/f2b476d4f4f685d84b4957e6c805740597945cde/src/core/observer/array.js and https://github.com/vuejs/vue/blob/daed1e73557d57df244ad8d46c9afff7208c9a2d/src/core/util/lang.js
RaiseMyEvent();
return result; // Original push() implementation
}
});
有关如何实现push之外的其他变异函数的提示,请参阅我的源代码注释:“pop”、“shift”、“unshift”、“splice”、“sort”、“reverse”。谢谢!这适用于正则数组方法。关于如何为“arr[2]=“foo”之类的内容引发事件,您有什么想法吗?我想您可以实现一个方法
set(index)
在数组的原型中,并执行类似于antisanity saysIt的操作。修改数组的子类会更好。修改数组的原型通常不是一个好主意。这里的答案非常好。Observatearray的类非常好。+1“'\u Array.length==0&&delete\u self[index];“-你能解释这一行吗?这很好,但有一个重要的警告:当数组被修改为arr[2]=”foo“
,更改通知是异步的。由于JS不提供任何方法来监视此类更改,因此该库依赖于每250毫秒运行一次的超时,并检查阵列是否发生了任何更改——因此您不会收到更改通知
var array = [1,2,3,4];
array = new Proxy(array, {
set: function(target, key, value) {
if (Number.isInteger(Number(key)) || key === 'length') {
debugger; //or other code
}
target[key] = value;
return true;
}
});
class OArray {
constructor(capacity, observer) {
var Obj = {};
var Ref = []; // reference object to hold values and apply array methods
if (!observer) observer = function noop() {};
var propertyDescriptors = Object.getOwnPropertyDescriptors(Array.prototype);
Object.keys(propertyDescriptors).forEach(function(property) {
// the property will be binded to Obj, but applied on Ref!
var descriptor = propertyDescriptors[property];
var attributes = {
configurable: descriptor.configurable,
enumerable: descriptor.enumerable,
writable: descriptor.writable,
value: function() {
observer.call({});
return descriptor.value.apply(Ref, arguments);
}
};
// exception to length
if (property === 'length') {
delete attributes.value;
delete attributes.writable;
attributes.get = function() {
return Ref.length
};
attributes.set = function(length) {
Ref.length = length;
};
}
Object.defineProperty(Obj, property, attributes);
});
var indexerProperties = {};
for (var k = 0; k < capacity; k++) {
indexerProperties[k] = {
configurable: true,
get: (function() {
var _i = k;
return function() {
return Ref[_i];
}
})(),
set: (function() {
var _i = k;
return function(value) {
Ref[_i] = value;
observer.call({});
return true;
}
})()
};
}
Object.defineProperties(Obj, indexerProperties);
return Obj;
}
}
Object.defineProperty(myArray, "push", {
configurable: true,
enumerable: false,
writable: true, // Previous values based on Object.getOwnPropertyDescriptor(Array.prototype, "push")
value: function (...args)
{
let result = Array.prototype.push.apply(this, args); // Original push() implementation based on https://github.com/vuejs/vue/blob/f2b476d4f4f685d84b4957e6c805740597945cde/src/core/observer/array.js and https://github.com/vuejs/vue/blob/daed1e73557d57df244ad8d46c9afff7208c9a2d/src/core/util/lang.js
RaiseMyEvent();
return result; // Original push() implementation
}
});