最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - addEventListener on proxied Event Target - Stack Overflow

programmeradmin5浏览0评论

I'm trying to create an object that is basically both a Proxy and an EventTarget. The goal is to be able to subscribe to any changes made to this object.

Here's how I define this object:

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

At this point I'd like to be able to call state.addEventListener('change', console.log) but this gives me an error:

Uncaught TypeError: Illegal invocation

So here's what works:

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

But as I said, I'd like to have one single unified object that can be both the target (can be listened to with addEventListener) and the store of values (proxied object in charge of dispatching the events when modified). So far, this method only works if you carry around both target and state...

Any idea why I can't call addEventListener by going through the Proxy?


Technically, calling state.addEventListener() goes through the get prototype method, so I tried defining get: Reflect.get in the proxy handler but it adds nothing... (Even though it was indeed reached because I tried adding a console.log there too)

So why can't I call addEventListener through the proxy but it works fine directly on the target object?

I'm trying to create an object that is basically both a Proxy and an EventTarget. The goal is to be able to subscribe to any changes made to this object.

Here's how I define this object:

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

At this point I'd like to be able to call state.addEventListener('change', console.log) but this gives me an error:

Uncaught TypeError: Illegal invocation

So here's what works:

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

But as I said, I'd like to have one single unified object that can be both the target (can be listened to with addEventListener) and the store of values (proxied object in charge of dispatching the events when modified). So far, this method only works if you carry around both target and state...

Any idea why I can't call addEventListener by going through the Proxy?


Technically, calling state.addEventListener() goes through the get prototype method, so I tried defining get: Reflect.get in the proxy handler but it adds nothing... (Even though it was indeed reached because I tried adding a console.log there too)

So why can't I call addEventListener through the proxy but it works fine directly on the target object?

Share Improve this question edited Dec 1, 2019 at 11:18 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Nov 29, 2019 at 18:28 SheraffSheraff 6,8023 gold badges35 silver badges59 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 4

Since you are using a proxy you do need to use the getter like you thought but you more than likely didnt bind the function so it would be invoked with the correct this. As you lose that connection when you return the function without it, it would instead refer to the proxy object which isnt itself implementing the EventTarget/EventListener methods.

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

Oh, nevermind... I found the answer... Still posting it though, as it wasn't as straightforward for me as I would have hoped and it might help someone.


The issue is that when the proxy returns a function, the function doesn't have the proper scope (this). So you need to bind the proper scope to the returned function.

This can be done by defining a getter:

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

Once this is done, it's as simple as this:

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

On a W3C remendations level, I am unsure as to why a Proxyshouldn't return functions with their proper scope...

发布评论

评论列表(0)

  1. 暂无评论