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

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

问题描述

我正在通过 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.getReflect.set 时,我们没有传递接收者(实际上,我们没有使用 接收者 参数,所以我已经注释掉了参数).这意味着他们将使用目标本身作为接收器,如果属性是访问器(例如 Mapsize 属性)并且他们需要他们的 this 是实际的实例(就像 Mapsize 一样).

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#getMap#set,那么您根本不需要代理.要么:

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

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

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

创建一个继承自Map实例的新对象,并覆盖getset;您不必控制原始 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 实例上的 setget 方法.

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屋!

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