为什么在 ES2015 中代理到 Map 对象不起作用 [英] Why is Proxy to a Map object in ES2015 not working
问题描述
我正在通过 Google Chrome 版本 57.0.2987.133 运行以下脚本:
I'm running the following script through Google Chrome Version 57.0.2987.133:
var loggingProxyHandler = {
"get" : function(targetObj, propName, receiverProxy) {
let ret = Reflect.get(targetObj, propName, receiverProxy);
console.log("get("+propName.toString()+"="+ret+")");
return ret;
},
"set" : function(targetObj, propName, propValue, receiverProxy) {
console.log("set("+propName.toString()+"="+propValue+")");
return Reflect.set(targetObj, propName, propValue, receiverProxy);
}
};
function onRunTest()
{
let m1 = new Map();
let p1 = new Proxy(m1, loggingProxyHandler);
p1.set("a", "aval"); // Exception thrown from here
}
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy
运行时,我看到处理程序的 get 陷阱被调用以返回 Map 的 set 函数然后我收到以下错误:
When run, I see the handler's get trap is called to return the Map's set function and then I receive the following error:
"Uncaught TypeError: Method Map.prototype.set called on incompatible receiver [object Object]"
at Proxy.set (native)
...
我尝试从 loggingProxyHandler 中删除陷阱函数(使其成为空对象),但仍然收到相同的错误.
I tried removing the trap functions from the loggingProxyHandler (making it an empty object) but still receive the same error.
我的理解是,应该能够为所有原生 ES5 和 ES2015 javascript 对象生成代理对象.Array 似乎在相同的代理处理程序下运行良好.我误解了规格吗?
我的代码缺少什么吗?Chrome 中是否存在已知错误?(我进行了搜索,发现 Chrome 在此主题上没有任何缺陷.)
My understanding was that a Proxy object was supposed to be able to generated for all native ES5 and ES2015 javascript objects. Array seems to work well under the same proxy handler.
Did I misunderstand the specs?
Is my code missing something?
Is there a known bug in Chrome? (I did a search and found no defects for Chrome on this subject.)
推荐答案
您收到错误的原因是代理没有参与 p1.set
调用(除了set
陷阱 — 无关,尽管名称相同 — 被调用以检索函数引用).因此,一旦检索到函数引用,就会调用它,并将 this
设置为代理,而不是 Map
—Map
不喜欢哪个.
The reason you're getting the error is that the proxy isn't getting involved in the p1.set
call (other than that the set
trap — unrelated, despite same name — is getting called to retrieve the function reference). So once the function reference has been retrieved, it's called with this
set to the proxy, not the Map
— which Map
doesn't like.
如果您真的想拦截 Map
上的所有属性访问调用,您可以通过绑定从 get
返回的任何函数引用来修复它(请参阅***
行):
If you're really trying to intercept all property access calls on the Map
, you can fix it by binding any function references you're returning from get
(see the ***
lines):
const loggingProxyHandler = {
get(target, name/*, receiver*/) {
let ret = Reflect.get(target, name);
console.log(`get(${name}=${ret})`);
if (typeof ret === "function") { // ***
ret = ret.bind(target); // ***
} // ***
return ret;
},
set(target, name, value/*, receiver*/) {
console.log(`set(${name}=${value})`);
return Reflect.set(target, name, value);
}
};
function onRunTest() {
const m1 = new Map();
const p1 = new Proxy(m1, loggingProxyHandler);
p1.set("a", "aval");
console.log(p1.get("a")); // "aval"
console.log(p1.size); // 1
}
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy
请注意,在调用 Reflect.get
和 Reflect.set
时,我们没有传递接收者(实际上,我们没有使用 接收者
参数,所以我已经注释掉了参数).这意味着他们将使用目标本身作为接收器,如果属性是访问器(例如 Map
的 size
属性)并且他们需要他们的 this
是实际的实例(就像 Map
的 size
一样).
Notice that when calling Reflect.get
and Reflect.set
, we don't pass along the receiver (in fact, we're not using the receiver
argument at all in those, so I've commented the parameter out). That means they'll use the target itself as the receiver, which you need if the properties are accessors (like Map
's size
property) and they need their this
to be the actual instance (as Map
's size
does).
如果您的目标只是拦截Map#get
和Map#set
,那么您根本不需要代理.要么:
If your goal is just to intercept Map#get
and Map#set
, though, you don't need a proxy at all. Either:
创建一个
Map
子类并实例化它.不过,假设您控制了Map
实例的创建.
Create a
Map
subclass and instantiate that. Assumes you control the creation of theMap
instance, though.
创建一个继承自Map
实例的新对象,并覆盖get
和set
;您不必控制原始 Map
的创建.
Create a new object that inherits from the Map
instance, and override get
and set
; you don't have to be in control of the original Map
's creation.
用您自己的版本替换 Map
实例上的 set
和 get
方法.
Replace the set
and get
methods on the Map
instance with your own versions.
这里是 #1:
class MyMap extends Map {
set(...args) {
console.log("set called");
return super.set(...args);
}
get(...args) {
console.log("get called");
return super.get(...args);
}
}
const m1 = new MyMap();
m1.set("a", "aval");
console.log(m1.get("a"));
#2:
const m1 = new Map();
const p1 = Object.create(m1, {
set: {
value: function(...args) {
console.log("set called");
return m1.set(...args);
}
},
get: {
value: function(...args) {
console.log("get called");
return m1.get(...args);
}
}
});
p1.set("a", "aval");
console.log(p1.get("a"));
#3:
const m1 = new Map();
const m1set = m1.set; // Yes, we know these are `Map.prototype.set` and
const m1get = m1.get; // `get`, but in the generic case, we don't necessarily
m1.set = function(...args) {
console.log("set called");
return m1set.apply(m1, args);
};
m1.get = function(...args) {
console.log("get called");
return m1get.apply(m1, args);
}
m1.set("a", "aval");
console.log(m1.get("a"));
这篇关于为什么在 ES2015 中代理到 Map 对象不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!