我使用Map javascript对象。我想观察使用Proxy对象对Map实例的更改。但是,无论我尝试输入代理的任何处理程序对象,我都会一直收到错误Method Map.prototype.set called on incompatible receiver
。我究竟做错了什么?我希望工作的惯用例看起来像
var map = new Map();
var proxy = new Proxy(map, {
set(obj, prop, value) {
console.log(value);
}
});
proxy.set(1, 1); --> error
我也尝试为apply
设置处理程序,但无济于事。
答案 0 :(得分:1)
首先,请了解您的错误也会仅使用
重现var map = new Map();
var proxy = new Proxy(map, {});
proxy.set(1, 1);
与set(obj, prop, value)
的使用无关。
要进一步打破这一点,请理解这与执行
基本相同var map = new Map();
var proxy = new Proxy(map, {});
Map.prototype.set.call(proxy, 1, 1);
其中也有错误。您正在为set
个实例调用Map
函数,但是传递Proxy
而不是Map
个实例。
这就是问题的核心。 Map
使用专门与map
对象本身关联的私有内部插槽存储其数据。代理不会100%透明地表现。它们允许您拦截对象上的某组操作,并在它们发生时执行逻辑,这通常意味着将该逻辑代理到某个其他对象,在您的情况下从proxy
到map
。 / p>
不幸的是,对于您的情况,代理访问Map实例的私有内部插槽不是可以拦截的行为之一。你可以想象它像
var PRIVATE = new WeakMap();
var obj = {};
PRIVATE.set(obj, "private stuff");
var proxy = new Proxy(obj, {});
PRIVATE.get(proxy) === undefined // true
PRIVATE.get(obj) === "private stuff" // true
因为对象作为this
传递给Map.prototype.set
不是真正的Map,它无法找到所需的数据并会抛出异常。
此处的解决方案意味着您确实需要将正确的this
传递给Map.prototype.set
。
对于代理,最简单的方法是实际拦截对.set
的访问,例如
var map = new Map();
var proxy = new Proxy(map, {
get(target, prop, receiver) {
// Perform the normal non-proxied behavior.
var value = Reflect.get(target, prop, receiver);
// If something is accessing the property `proxy.set`, override it
// to automatically do `proxy.set.bind(map)` so that when the
// function is called `this` will be `map` instead of `proxy`.
if (prop === "set" && typeof value === "function") value = value.bind(target);
return value;
}
});
proxy.set(1, 1);
当然,这并未解决您关于拦截对.set
的实际调用的问题,因此您可以对此进行扩展以进行此操作
var map = new Map();
var proxy = new Proxy(map, {
get(target, prop, receiver) {
var value = Reflect.get(target, prop, receiver);
if (prop === "set" && typeof value === "function") {
// When `proxy.set` is accessed, return your own
// fake implementation that logs the arguments, then
// calls the original .set() behavior.
const origSet = value;
value = function(key, value) {
console.log(key, value);
return origSet.apply(map, arguments);
};
}
return value;
}
});
proxy.set(1, 1);