我正在创建一个像下面这样的IIFE。它将getter和setter返回到内部存储的数组变量。我希望拦截对该数组所做的更改 - console.log
旨在表明在下面的setter中。
const a = (function() {
let arr = [];
return {
get arr() {return arr},
set arr(v) {
console.log("new arr", v);
arr = v;
},
}
})();
如果我完全重新分配arr
,例如, a.arr = [1, 2]
。
但它不拦截对数组内容所做的更改,例如a.arr.push(3)
或a.arr.shift()
。
寻找有关如何拦截这些内容更改的任何想法。
这是使用新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));
})();
问题依然存在。仍然无法使用从a.arr
到a.arr[0] = 1
的任何内容来观察a.push(3)
内容的变化。
该解决方案基于Kris对Sindre的this commit的'on-change' library。
克里斯的Explanation of the solution:
在
set
陷阱中,我的目标是确定所提供的值是否是之前调用Proxy
陷阱产生的get
。如果它是这样的Proxy
,任何财产访问将被我们自己的get
陷阱截获。因此,当我访问value[proxyTarget]
时,将调用我们的get
陷阱,编码为在target
时返回property === proxyTarget
(第46行)。如果传递给set
的值不是改变创建的代理,那么value[proxyTarget]
就是undefined
。
解决方案的完整代码:
(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);
};
这解决了我的问题,而没有采用检查数组修改方法的方法。
我现在已经在David Walsh's post here的帮助下对此进行了排序。它仍然很难看,但现在仍然有用。
使用recursive-ish onChanged
陷阱更新了get
代理制造商。
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;
},
还添加了一个函数列表来检查get陷阱(丑陋,hacky位):
const mutators = [
"push",
"pop",
"shift",
"unshift",
"splice",
"reverse",
"fill",
"sort"
]
到目前为止,这似乎在我的测试中起作用。
感谢您指出正确的方向。