为什么代理到ES2015中的Map对象不起作用 [英] Why is Proxy to a Map object in ES2015 not working

查看:522
本文介绍了为什么代理到ES2015中的Map对象不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我通过Google Chrome 57.0.2987.133版本运行以下脚本:

  var loggingProxyHandler = {get:function(targetObj,propName,receiverProxy){let ret = Reflect.get(targetObj,propName,receiverProxy);的console.log( 获取( + propName.toString()+ = + RET + ));返回ret; },set:function(targetObj,propName,propValue,receiverProxy){console.log(set(+ propName.toString()+=+ propValue +));返回Reflect.set(targetObj,propName,propValue,receiverProxy); }}; function onRunTest(){let m1 = new Map();让p1 =新代理(m1,loggingProxyHandler); p1.set(a,aval); //从这里抛出的异常} onRunTest();  

:需要支持ES2015的代理的浏览器



运行时,处理程序的get trap被调用以返回Map的set函数
,然后我收到以下错误:

 Uncaught TypeError :在不兼容的receiver [object Object]上调用的方法Map.prototype.setProxy.set(native)
...

我尝试从loggingProxyHandler中删除陷阱函数(使其成为一个空对象),但仍然收到相同的错误。

我的理解是,代理对象应该能够为所有原生ES5和ES2015 javascript对象生成。数组似乎在相同的代理处理程序下工作良好。
我是否误解了规格?

我的代码是否遗漏了某些东西?
Chrome中是否存在已知的错误? (我做了一个搜索,发现Chrome在这个主题上没有任何缺陷。) 解决方案

你得到错误的原因是代理没有涉及 p1.set 调用(除了 set 陷阱&mbsp;—不相关,尽管名称相同 - —正在调用以检索函数引用)。因此,一旦检索到函数引用,就会调用 this 设置为代理,而不是 Map  &mdash ;其中 Map 不喜欢。



如果您真的试图拦截 Map ,你可以通过绑定你从 get 返回的任何函数引用来修复它(参见 lines):

var loggingProxyHandler = {get:function(targetObj,propName,receiverProxy){let ret = Reflect.get(targetObj,propName,receiverProxy);的console.log( 获取( + propName.toString()+ = + RET + )); (typeof ret ===function){// *** ret = ret.bind(targetObj); // ***} // *** return ret; },set:function(targetObj,propName,propValue,receiverProxy){console.log(set(+ propName.toString()+=+ propValue +));返回Reflect.set(targetObj,propName,propValue,receiverProxy); }}; function onRunTest(){let m1 = new Map();让p1 =新代理(m1,loggingProxyHandler); p1.set(a,aval);的console.log(p1.get( A)); // } onRunTest();

需要支持ES2015的代理服务器的浏览器

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


  1. 创建一个 Map 子类并实例化它。不过,假设您控制 Map 实例的创建。

  2. 创建一个新的继承自 Map 实例,并覆盖 get set ;您不必控制原始的 Map 的创建。
  3. 替换使用您自己的 Map 实例 set 获取方法

这里是#1:

  class MyMap extends Map {set(... args){console.log(set called);返回super.set(... args); } get(... args){console.log(get called);返回super.get(... args); }} const m1 = new MyMap(); m1.set(a,aval); console.log(m1.get(a));  

-lang =jsdata-hide =falsedata-console =truedata-babel =false>

  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));  

  const m1 = new Map(); const m1set = m1.set; //是的,我们知道这些是`Map.prototype.set`和const m1get = m1.get; //`get`,但在一般情况下,我们并不一定m1.set = function(... args){console.log(set called);返回m1set.apply(m1,args);}; m1.get = function(... args){console.log(get called); (); m1.get(a,aval); console.log(m1.get(a));  


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

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)
...

I tried removing the trap functions from the loggingProxyHandler (making it an empty object) but still receive the same error.

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.)

解决方案

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.

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):

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

If your goal is just to intercept Map#get and Map#set, though, you don't need a proxy at all. Either:

  1. Create a Map subclass and instantiate that. Assumes you control the creation of the Map instance, though.

  2. 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.

  3. Replace the set and get methods on the Map instance with your own versions.

Here's #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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆