How to use JavaScript EventTarget?

前端 未结 10 1822
一生所求
一生所求 2020-12-01 03:53

I would like to create a custom event emitter in my client-side programs. I am referencing this (sparse) documentation for EventTarget

My implementation atte

10条回答
  •  感动是毒
    2020-12-01 04:16

    There are two ways to implement the EventTarget "Interface".

    1) Like mdn suggests use javascript prototypes. In my opinion this is clearly not the best approach to do this. The simple reason is that everybody who does use your library has to know that he needs to add a listeners property to his constructor function.

    function implement_event_target_interface(target_constructor_function) 
    {
        target_constructor_function.prototype.listeners = null;
        target_constructor_function.prototype.addEventListener = function(type, callback) {
            if (!(type in this.listeners)) {
                this.listeners[type] = [];
            }
            this.listeners[type].push(callback);
        };
    
        target_constructor_function.prototype.removeEventListener = function(type, callback) {
            if (!(type in this.listeners)) {
                return;
            }
            var stack = this.listeners[type];
            for (var i = 0, l = stack.length; i < l; i++) {
                if (stack[i] === callback){
                stack.splice(i, 1);
                return;
                }
            }
        };
    
        target_constructor_function.prototype.dispatchEvent = function(event) {
            if (!(event.type in this.listeners)) {
                return true;
            }
            var stack = this.listeners[event.type].slice();
    
            for (var i = 0, l = stack.length; i < l; i++) {
                stack[i].call(this, event);
            }
            return !event.defaultPrevented;
        };
    }
    
    let Person = function()
    {
        this.listeners = {}; // Every contructor that implements the event_target_interface must have this property. This is not very practical and intuitive for the library-user.
    
        this.send_event = function() {
            var event = new CustomEvent('test_event', { 'detail': "test_detail" });
            this.dispatchEvent(event);
        }
    }
    
    implement_event_target_interface(Person);
    
    let person = new Person();
    
    person.addEventListener('test_event', function (e) { 
        console.log("catched test_event from person")
    }.bind(this), false);
    
    person.send_event();
    

    And not only that, it gets even worse when you use constructor inheritance on Person, because you also need to inherit the prototype in order to be able to send events.

    let Student = function() {
        Person.call(this);
    }
    
    Student.prototype = Person.prototype;
    Student.prototype.constructor = Student;
    
    let student = new Student();
    
    student.addEventListener('test_event', function (e) { 
        console.log("catched test_event from student")
    }.bind(this), false);
    
    student.send_event();
    

    2) Use constructor inheritance. Much much better.

    function EventTarget() 
    {
        this.listeners = {};
    
        this.addEventListener = function(type, callback) {
            if (!(type in this.listeners)) {
                this.listeners[type] = [];
            }
            this.listeners[type].push(callback);
        };
    
        this.removeEventListener = function(type, callback) {
            if (!(type in this.listeners)) {
                return;
            }
            var stack = this.listeners[type];
            for (var i = 0, l = stack.length; i < l; i++) {
                if (stack[i] === callback){
                stack.splice(i, 1);
                return;
                }
            }
        };
    
        this.dispatchEvent = function(event) {
            if (!(event.type in this.listeners)) {
                return true;
            }
            var stack = this.listeners[event.type].slice();
    
            for (var i = 0, l = stack.length; i < l; i++) {
                stack[i].call(this, event);
            }
            return !event.defaultPrevented;
        };
    }
    
    let Person = function()
    {
        EventTarget.call(this);
    
        this.send_event = function() {
            var event = new CustomEvent('test_event', { 'detail': "test_detail" });
            this.dispatchEvent(event);
        }
    }
    
    let person = new Person();
    
    person.addEventListener('test_event', function (e) { 
        console.log("catched test_event from person")
    }.bind(this), false);
    
    person.send_event(); 
    

提交回复
热议问题