[英]How to observe changes to contents of an object's array property exposed through setter-getter or Proxy
I'm creating an IIFE like below. 我正在创建如下的IIFE。 It returns getters and setters to an array variable stored internally. 它将getter和setter返回到内部存储的数组变量。 I wish to intercept changes made to that array - the console.log
is intended to indicate that in the setter below. 我希望拦截对该数组所做的更改console.log
旨在在下面的设置器中表明这一点。
const a = (function() {
let arr = [];
return {
get arr() {return arr},
set arr(v) {
console.log("new arr", v);
arr = v;
},
}
})();
This works fine if I completely reassign arr
, eg a.arr = [1, 2]
. 如果我完全重新分配arr
,例如a.arr = [1, 2]
,则此方法很好。
But it doesn't intercept changes made to contents of the array, eg a.arr.push(3)
or a.arr.shift()
. 但是它不会拦截对数组内容所做的更改,例如a.arr.push(3)
或a.arr.shift()
。
Looking for any ideas on how to intercept these content changes. 寻找有关如何拦截这些内容更改的任何想法。
This is an alternate attempt using the new Proxy object: 这是使用新的Proxy对象的替代尝试:
a = (function() {
let details = {
arr: []
}
function onChangeProxy(object, onChange) {
const handler = {
apply: function (target, thisArg, argumentsList) {
onChange(thisArg, argumentsList);
return thisArg[target].apply(this, argumentsList);
},
defineProperty: function (target, property, descriptor) {
Reflect.defineProperty(target, property, descriptor);
onChange(property, descriptor);
return true;
},
deleteProperty: function(target, property) {
Reflect.deleteProperty(target, property);
onChange(property, descriptor);
return;
}
};
return new Proxy(object, handler);
};
return onChangeProxy(details, (p, d) => console.log(p, d));
})();
The problem remains the same. 问题仍然存在。 Still unable to observe changes made to the contents of a.arr
using anything from a.arr[0] = 1
to a.push(3)
. 仍然无法使用从a.arr[0] = 1
到a.push(3)
任何内容观察对a.arr
内容所做的更改。
The solution is based on this commit by Kris on Sindre's 'on-change' library . 解决方案基于Kris在Sindre的“ on-change”库中的 提交 。
Explanation of the solution , by Kris: 解决方案的解释 ,由Kris撰写:
In the
set
trap, my goal is to determine if the value provided is aProxy
produced by a previous call to theget
trap. 在set
陷阱中,我的目标是确定所提供的值是否是先前调用get
陷阱所产生的Proxy
。 If it is such aProxy
, any property access will be intercepted by our ownget
trap. 如果是这样的Proxy
,则任何属性访问都将被我们自己的get
陷阱拦截。 So, when I accessvalue[proxyTarget]
ourget
trap will be invoked, which is coded to returntarget
whenproperty === proxyTarget
(line 46). 因此,当我访问value[proxyTarget]
将调用我们的get
陷阱,当property === proxyTarget
时,该陷阱将被编码为返回target
(第46行)。 If the value passed toset
is not an on-change-created Proxy, thenvalue[proxyTarget]
isundefined
. 如果传递给set
的值不是基于变更创建的代理,则value[proxyTarget]
是undefined
。
Full code of the solution: 解决方案的完整代码:
(object, onChange) => {
let inApply = false;
let changed = false;
function handleChange() {
if (!inApply) {
onChange();
} else if (!changed) {
changed = true;
}
}
const handler = {
get(target, property, receiver) {
const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
const value = Reflect.get(target, property, receiver);
// Preserve invariants
if (descriptor && !descriptor.configurable) {
if (descriptor.set && !descriptor.get) {
return undefined;
}
if (descriptor.writable === false) {
return value;
}
}
try {
return new Proxy(value, handler);
} catch (_) {
return value;
}
},
set(target, property, value) {
const result = Reflect.set(target, property, value);
handleChange();
return result;
},
defineProperty(target, property, descriptor) {
const result = Reflect.defineProperty(target, property, descriptor);
handleChange();
return result;
},
deleteProperty(target, property) {
const result = Reflect.deleteProperty(target, property);
handleChange();
return result;
},
apply(target, thisArg, argumentsList) {
if (!inApply) {
inApply = true;
const result = Reflect.apply(target, thisArg, argumentsList);
if (changed) {
onChange();
}
inApply = false;
changed = false;
return result;
}
return Reflect.apply(target, thisArg, argumentsList);
}
};
return new Proxy(object, handler);
};
This has solved my problem, without resorting to the hack of checking for array modifying methods. 这解决了我的问题,而无需借助检查数组修改方法的技巧。
I've sorted this, for now, with help from David Walsh's post here . 在David Walsh在这里的帖子的帮助下,到目前为止,我已经对此进行了排序。 It's still ugly, but works for now. 它仍然很难看,但是现在可以使用。
Updated the onChanged
Proxy maker with a recursive-ish get
trap. 更新了具有递归式get
陷阱的onChanged
Proxy maker。
get: function (target, property, receiver) {
let retval;
try {
retval = new Proxy(target[property], handler);
} catch (err) {
retval = Reflect.get(target, property, receiver);
}
if (mutators.includes(property))
onChange(target, property, receiver);
return retval;
},
Also added a list of functions to check the get trap against (the ugly, hacky bit): 还添加了用于检查获取陷阱的函数列表(丑陋,hacky):
const mutators = [
"push",
"pop",
"shift",
"unshift",
"splice",
"reverse",
"fill",
"sort"
]
This seems to be working in my testing so far. 到目前为止,这似乎在我的测试中起作用。
Thanks for pointing in the correct direction. 感谢您指出正确的方向。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.