2017-04-05 66 views
1

我正在通过谷歌的Chrome版本57.0.2987.133以下脚本:为什么是代理在ES2015一个Map对象不工作

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陷阱被称为返回地图的设定功能 然后我收到以下错误:

"Uncaught TypeError: Method Map.prototype.set called on incompatible receiver [object Object]" 
at Proxy.set (native) 
... 

我试过removin g loggingProxyHandler中的陷阱函数(使其成为空对象),但仍然收到相同的错误。

我的理解是,代理对象应该能够为所有原生ES5和ES2015 JavaScript对象生成。数组似乎在相同的代理处理程序下工作良好。 我误解了规格吗?
我的代码丢失了什么? Chrome中有没有已知的错误? (我做了搜索,发现Chrome的无缺陷对这个问题)。

+0

的可能重复[?为什么我的代理,它包装一个地图的函数调用抛出类型错误](http://stackoverflow.com/questions/42381028/why-is-这听起来像你实际上想要做的就是覆盖(拦截)set和get调用,而不是路由所有的*属性访问*通过(通过代理包装的一个映射函数调用抛出typeerror) – Bergi

+1

一个代理? – Bergi

+0

要说清楚:不要使用'Proxy'来拦截异常行为,这超出了正常的'对象'语义。改为使用子类。 – ftor

回答

4

你得到错误的原因是,代理未卷入了p1.set调用(比set陷阱其他  —无关,尽管名称相同  —正在调用以检索函数引用)。所以一旦检索到函数引用,就会调用this设置为代理,而不是Map   —其中Map不喜欢。

如果你真的试图拦截在Map所有属性访问调用,您可以结合你从get返回任何函数引用修复它(见***线):

var loggingProxyHandler = { 
 
    "get" : function(targetObj, propName, receiverProxy) { 
 
     let ret = Reflect.get(targetObj, propName, receiverProxy); 
 
     console.log("get("+propName.toString()+"="+ret+")"); 
 
     if (typeof ret === "function") { // *** 
 
      ret = ret.bind(targetObj);  // *** 
 
     }        // *** 
 
     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"); 
 
    console.log(p1.get("a")); // "aval" 
 
} 
 

 
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy

但是,如果您的目标只是拦截Map#getMap#set,您根本不需要代理。或者:

  1. 创建一个Map子类并实例化它。尽管如此,假设您控制Map实例的创建。

  2. 创建一个继承Map实例的新对象,并覆盖getset;你不必控制原始Map的创作。

  3. 用您自己的版本替换Map实例上的setget方法。

这里的#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"));

+0

它看起来像我需要去与一个子类机制,如果我想在这里达到某种程度的AOP。但是,然后访问Map的size属性是有问题的,因为它不是通过方法调用实现的。当我添加推荐的绑定时,它确实得到了异常,但是一旦函数通过get陷阱绑定到targetObj,set陷阱将不会被调用,从而有效地取消代理。我想如果我想追求代理,我可以尝试生成一个全新的函数,绑定到targetObject,但调用代理处理程序陷阱(通​​过闭包)。 – Rand

+0

@Rand:是的。没有理由设置陷阱会被调用,没有任何设置属性。我不明白为什么访问'size'有问题:属性是继承的,只是访问它。 –

+0

@ T.J.Crowder你说:“我不明白为什么访问大小有问题:属性是继承的,只是访问它。”我不明白为什么,但它是。在Chrome 59上,你的第一个例子工作,但如果我添加'console.log(p1.size)',它会给出'Uncaught TypeError:方法Map.prototype.size在不兼容的receiver [object Object]上调用'。有任何想法吗?你的例子是我见过的最接近Map代理的工作。 –

相关问题