如何使用类方法作为回调在类中添加事件处理程序?

IT技术 javascript oop event-handling
2021-03-04 13:47:35

如何使用类方法作为回调在类中添加事件处理程序?

<div id="test">move over here</div>
<script>
    oClass = new CClass();
    function CClass()
    {
        this.m_s = "hello :-/";
        this.OnEvent = OnEvent;
        with(this)
        {
            var r = document.getElementById("test");
            r.addEventListener('mouseover', this.OnEvent);  // this does NOT work :-/
        }
      
        function OnEvent()
        {
            alert(this);    // this will be the HTML div-element
            alert(this.m_s);    // will be undefined :-()
        }
    }
</script>

是的,我知道一些使其工作的怪癖,但是当引入这些事件处理程序时,预期的方式是什么???我再次有一种痛苦的感觉,没有人真正生活在 OOP 中 :-(

在这里给你玩:https : //jsfiddle.net/sepfsvyo/1/

3个回答

this事件侦听器回调内部将是触发事件的元素。如果你想this成为你的类的实例,那么要么:

将函数绑定到类实例:

使用Function.prototype.bind, 将创建一个新函数,它的this值将始终是您指定的值(类实例):

r.addEventListener('mouseover', this.OnEvent.bind(this));
//                                          ^^^^^^^^^^^

将函数包裹在匿名函数中:

var that = this;
r.addEventListener('mouseover', function(ev) { that.OnEvent(ev); });
//                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

或使用箭头函数(所以不需要that):

r.addEventListener('mouseover', ev => this.OnEvent(ev));
//                              ^^^^^^^^^^^^^^^^^^^^^^

注意:正如下面的评论中提到的,上述两种方法都将不同的函数传递给addEventListener(具有bind创建新函数的函数,而同义函数显然是!== this.OnEvent)。如果稍后要删除事件侦听器,则必须存储对该函数的引用:

var reference;
r.addEventListener('mouseover', reference = this.OnEvent.bind(this));
//                              ^^^^^^^^^^^^

或者:

var reference;
var that = this;
r.addEventListener('mouseover', reference = function(ev) { that.OnEvent(ev); });
//                              ^^^^^^^^^^^^

然后您可以删除事件侦听器,例如:

r.removeEventListener('mouseover', reference);
@ibrahimmahrir 啊,这就是我所缺少的。所以在课堂上,window.addEventListener('message', this.msgref= function(e){that.handleMessage(e)})它会起作用吗?
2021-04-21 13:47:35
@AwokeKnowing 是的,Ifthis是类的实例(如果该代码行在方法或构造函数中,而不是在另一个this绑定到其他东西的函数中)。
2021-04-23 13:47:35
@AwokeKnowing 当我发布答案时,我打算添加一个注释,但觉得没有必要。现在我对此有了第二个意见,我添加了注释。
2021-04-28 13:47:35
谢谢你的绑定功能。我想正确的 oop 风格是在方法声明中this.OnEvent = OnEvent.bind(this);绑定 :-) 定义末尾的绑定将冻结(仅)Microsoft Edge 38.14393.0.0 :-( 请参见此处:jsfiddle.net/sepfsvyo/3
2021-05-16 13:47:35
如果您添加了几个这样的事件处理程序,您将如何删除其中一个?似乎使用 anon 功能后,您失去了关闭 this.OnEvent 的能力。?
2021-05-16 13:47:35

您实际上可以将对象作为 EventListener 回调返回,这样 JS 将handleEvent在类中搜索一个方法并相应地执行:

var myInstance = new myClass;
myInstance.addEventListener("mousedown",myInstance);

//  To remove the event you can follow the same pattern
myInstance.removeEventListener("mousedown",myInstance);

你必须这样构造你的类:

class myClass {
   
    constructor(){
        //  Whatever this is supposed to do.
        //  You can also add events listener within the class this way :
        this.addEventListener("mousedown",this);
    }
    
    mouseDownEvent(e)(){
        //  Some action related to the mouse down event (e)
        console.log(e.target);
    }
    mouseMoveEvent(e)(){
        //  Some action related to the mouse move event (e)
    }
    mouseUpEvent(e)(){
        // Some action related to the mouse up event (e)
    }

    handleEvent(e) {
        switch(e.type) {
            case "mousedown":
                this.mouseDownEvent(e);
            break;
            case "mousemove":
                this.mouseMoveEvent(e);
            break;
            case "mouseup":
                this.mouseUpEvent(e);
            break;
        }
    }
}

来源:

https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

https://www.thecssninja.com/javascript/handleevent

https://metafizzy.co/blog/this-in-event-listeners/

我发现这个方法更清晰,同时在类中声明事件this也非常明确。希望我帮助了某人。

我也觉得这个更干净。
2021-04-17 13:47:35
感谢我指出这一点。我以前从未听说过 handleEvent,这真的很棒。它完美地工作!
2021-05-01 13:47:35

来自@ibrahimmahrir的回答完成了这项工作,但我想巩固几点。

正如许多 JavaScript 开发人员难以理解的那样,this关键字是一个移动目标。在传统的 OOP 语言中,对象方法是对象独有的,因此this被定义为它所附加的对象。

JavaScript 函数更加混杂,可以附加到多个对象。在 JavaScript 中,this指的是当前正在调用函数的对象,不一定是它最初附加到的对象

对于 Event Handler 函数,调用对象是它所附加元素,而不是原始对象;因此this指的是元素。通常的安全的方法是存储在一个不同的变量的原始对象的引用,通常被称为that

oClass = new CClass();
function CClass() {
    var that = this;        //  a reference to the original object
    this.m_s = "hello :-/";
    this.OnEvent = OnEvent;
    var r = document.getElementById("test");
    r.addEventListener('click', this.OnEvent);

    function OnEvent() {
        alert(that);        //  this is now the object
        alert(that.m_s);    //  this works
    }
}

上面的评论是我更新的评论。我还删除了with没有贡献太多并且非常不鼓励的声明。

哦,我已将事件更改为click以使其更易于测试。

虽然我们与 混淆this,但它不一定是开始事情的元素。假设我们现在包括一个span

<div id="test">click <span>over</span> here</div>

单击span将触发事件侦听器,即使您实际上并未单击div它所连接的 。在这种情况下,事件从跨度冒泡到 div。

这里this仅指div具有事件监听器元素。如果要引用span,则需要event.target

function OnEvent(event) {   //  include event parameter
    alert(this);            //  the element attached
    alert(event.target);    //  the element clicked
    alert(that);            //  this is now the object
    alert(that.m_s);        //  this works
}

这是一个更新的小提琴:https : //jsfiddle.net/osk083xv/