ES6代理中的意外设置陷阱行为

时间:2017-11-18 08:43:34

标签: javascript node.js es6-proxy es6-map

let ar = []; 
let p = new Proxy(new Map(), { 

get: (o, k) => {
 ar.push(1)
  return Reflect.get(o, k).bind(o) 
},

set: (o, k, v) => {
 ar.push(2)
  return Reflect.set(o, k, v)
}
});
p.set(1, 2)
p.get(1)
console.log(ar) //Outputs [1,1]

我试图拦截Map对象上的set和get操作。我绝不试图扩展/子类化Map。 在代理所述Map对象的过程中,我遇到了这种奇怪的意外行为,设置的陷阱没有在上面的代码中被触发,而是获取陷阱被激发了两次!

我进一步按以下方式从get陷阱中记录k(键)值;

//same code above
get: (o, k) => {
 console.log(k) //Logs set and then get!
  return Reflect.get(o, k).bind(o) 
}
//same code below

我期望的行为是数组在get陷阱中为[2,1]console.log(k)以实际输出密钥的值。

我想知道为什么会这样,我在这里遇到了一些与代理地图有关的问题,但没有一个问题会导致任何明智的推理,为什么会发生这种情况。

我的最终目标是在设置陷阱中发起一个事件。我是否使用Proxy来表示它的用途?如果没有,我应该采取什么方法?我是否应该放弃使用Map到Object Literal,即使它会带来使用一个的所有缺点?例如:没有长度属性,仅字符串属性,没有强制唯一键等。

更新:越来越多的我深入研究这个代理地图,我遇到的问题就越多。在我看来,好像ES6 Proxy API以与普通对象相同的方式处理Maps。维尔和我的挖掘的答案支持了这一点。我为什么这么说?阅读以下内容;

//same code above
 get: (o, k) => {
     if(k === 'tray') return ']'
      return Reflect.get(o, k).bind(o) 
    }
    //same code below
p.tray //returns ] 

上述代码在理论上不应该成功,对吧?就像Proxy在拦截地图时使用相同的逻辑拦截对象操作一样!作为;

///--While the following---//
let m = new Map();
m.set('tray', ']')
m.tray //undefined

Vill的回答是,代理将Map.prototype.set标识为首次阅读 set 并将其作为下一个函数调用。 我没有在原始代码(最上面)中编写的集合陷阱中的意思是,它不会拦截修改/设置Map的属性,实际上是隐含/本机的属性。使用地图 - Map.prototype.set代替我们通过代理商授予的Reflect.set

所有这些是否进一步强化了代理和地图不能很好地融合在一起的事实?我是走错路了吗?如果是这样我有什么误解? Proxies是否应该像任何其他对象一样处理地图?

1 个答案:

答案 0 :(得分:3)

这不是它的特色(笑话)。

你应该明白代理的.get和.set究竟是做什么的。获取将拦截对象的任何读取尝试。让我们举个例子:

p.set(1,2)
p.get(1)

在第一行,我们:读取来自对象属性,名称为 set ,然后尝试将其作为函数调用

在第二行,我们读取来自object属性,名称为 get ,然后尝试将其作为函数调用。

如果您将尝试此代码:

p.val = 5;

然后你将尝试将5设置为名称为val的目标,并且将触发setter。

这就是代理的setter和getter如何工作。

因此,要实现所需的行为,请检查属性名称并返回具有一些其他实现的函数。并且不要忘记调用原始函数。

像这样的Somth:

get: (o, k) => {
 if (k==='get') return Reflect.get(o, k).bind(o);
if (k==='set') return function(v){Reflect.set(o, k, v)}
}

希望这有帮助。

更新:

let obj = {
  take: (v) => v
};
let handler = {
  get: (target, key) => {
    if (key === 'take') {
      return function(v) {
        console.log(`logger says: function ${key} was called with argument: ${v}`);
      }

      return target[key];
    }
  }
};

let proxy = new Proxy(obj, handler);

proxy.take(5);
proxy.take(3);