代理事件目标上的addEventListener

时间:2019-11-29 18:28:07

标签: javascript proxy javascript-objects dom-events addeventlistener

我正在尝试创建一个基本上既是Proxy又是EventTarget的对象。目的是能够订阅对此对象所做的任何更改。

这是我定义此对象的方式:

const target = new EventTarget()
const state = new Proxy(target, { 
    set: (obj, key, value) => { 
        Reflect.set(obj, key, value)
        obj.dispatchEvent(new CustomEvent('change', {detail: {key, value}}))
    }, 
    deleteProperty: (obj, key) => { 
        Reflect.deleteProperty(obj, key) 
        obj.dispatchEvent(new CustomEvent('change', {detail: {key, value: undefined}})) 
    } 
})

目前,我希望能够致电state.addEventListener('change', console.log),但这给了我一个错误:

  

未捕获的TypeError:非法调用

所以这是可行的:

target.addEventListener('change', console.log)
state.foo = 'bar'
// logs the event

但是正如我所说,我希望有一个统一的对象,既可以作为目标(可以用addEventListener监听)又可以作为值的存储(负责调度事件的代理对象)修改后)。到目前为止,仅当您随身携带两者 targetstate ...

时,此方法才有效

有人知道为什么我无法通过addEventListener来打电话给Proxy吗?


从技术上讲,调用state.addEventListener()是通过get原型方法进行的,因此我尝试在代理处理程序中定义get: Reflect.get,但没有添加任何内容...(即使确实可以访问,因为我也尝试在其中添加console.log

那我为什么不能通过代理调用addEventListener,但是它可以直接在target对象上正常工作?

2 个答案:

答案 0 :(得分:0)

哦,没关系...我找到了答案...尽管如此,仍然发布它,因为它对我而言并不像我希望的那样简单,它可能会对某人有所帮助。


问题在于,当代理返回一个函数时,该函数没有适当的范围(this)。因此,您需要将适当的作用域绑定到返回的函数。

这可以通过定义一个吸气剂来完成:

    get: function (obj, key) {
        const result = Reflect.get(obj, key)
        if(typeof result === 'function')
            return result.bind(obj)
        return result
    }

一旦完成,就这么简单:

const state = new Proxy(new EventTarget(), handler)
state.addEventListener('change', console.log)
state.foo = 'bar'
// logs the event

W3C建议级别上,我不确定Proxy为什么不应该返回具有适当范围的函数...

答案 1 :(得分:0)

由于使用的是代理服务器,因此确实需要按您的想法使用getter,但您很有可能没有绑定该函数,因此将使用正确的this来调用它。当您在没有函数的情况下返回该函数时,如果失去该连接,它将转而引用本身没有实现EventTarget / EventListener方法的代理对象。

let target = document.documentElement;
let targetProxy = new Proxy(target,{
  get:(obj,key)=>{
    let value = Reflect.get(obj,key);
    if(typeof(value) == "function"){
      return value.bind(obj);
    }
    return value;
  }
});
targetProxy.addEventListener('click',()=>console.log('clicked'));