How to use JavaScript EventTarget?

前端 未结 10 1820
一生所求
一生所求 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:02

    Without taking into consideration browser support where EventTarget can not be instantiated as a constructor and only to enrich this issue with yet another functional example.

    According to the compatibility list described by Mozilla itself in this date (October 7, 2018):

    EventTarget (constructor):

    • desktop:
      • Chrome 64
      • Firefox 59
      • Opera 51
    • mobile:
      • WebView 64
      • Chrome Android 64
      • Firefox Android 59
      • Opera Android 51

    Extends:

    class Emitter extends EventTarget {
        constructor() {
            super()
        }
    }
    

    You could create common methods in many event plugins like: on(), off(), .once() and emit() (using CustomEvent):

    /**
     * Emmiter - Event Emitter
     * @license The MIT License (MIT)             - [https://github.com/subversivo58/Emitter/blob/master/LICENSE]
     * @copyright Copyright (c) 2020 Lauro Moraes - [https://github.com/subversivo58]
     * @version 0.1.0 [development stage]         - [https://github.com/subversivo58/Emitter/blob/master/VERSIONING.md]
     */
    const sticky = Symbol()
    class Emitter extends EventTarget {
        constructor() {
            super()
            // store listeners (by callback)
            this.listeners = {
                '*': [] // pre alocate for all (wildcard)
            }
            // l = listener, c = callback, e = event
            this[sticky] = (l, c, e) => {
                // dispatch for same "callback" listed (k)
                l in this.listeners ? this.listeners[l].forEach(k => k === c ? k(e.detail) : null) : null
            }
        }
        on(e, cb, once = false) {
            // store one-by-one registered listeners
            !this.listeners[e] ? this.listeners[e] = [cb] : this.listeners[e].push(cb);
            // check `.once()` ... callback `CustomEvent`
            once ? this.addEventListener(e, this[sticky].bind(this, e, cb), { once: true }) : this.addEventListener(e, this[sticky].bind(this, e, cb))
        }
        off(e, Fn = false) {
            if ( this.listeners[e] ) {
                // remove listener (include ".once()")
                let removeListener = target => {
                    this.removeEventListener(e, target)
                }
                // use `.filter()` to remove expecific event(s) associated to this callback
                const filter = () => {
                    this.listeners[e] = this.listeners[e].filter(val => val === Fn ? removeListener(val) : val);
                    // check number of listeners for this target ... remove target if empty
                    this.listeners[e].length === 0 ? e !== '*' ? delete this.listeners[e] : null : null
                }
                // use `while()` to iterate all listeners for this target
                const iterate = () => {
                    let len = this.listeners[e].length;
                    while (len--) {
                        removeListener(this.listeners[e][len])
                    }
                    // remove all listeners references (callbacks) for this target (by target object)
                    e !== '*' ? delete this.listeners[e] : this.listeners[e] = []
                }
                Fn && typeof Fn === 'function' ? filter() : iterate()
            }
        }
        emit(e, d) {
            this.listeners['*'].length > 0 ? this.dispatchEvent(new CustomEvent('*', {detail: d})) : null;
            this.dispatchEvent(new CustomEvent(e, {detail: d}))
        }
        once(e, cb) {
            this.on(e, cb, true)
        }
    }
    
    const MyEmitter = new Emitter()
    
    // one or more listeners for same target ...
    MyEmitter.on('xyz', data => {
        console.log('first listener: ', data)
    })
    MyEmitter.on('xyz', data => {
        console.log('second listener: ', data)
    })
    
    // fire event for this target
    MyEmitter.emit('xyz', 'zzzzzzzzzz...') // see listeners show
    
    // stop all listeners for this target
    MyEmitter.off('xyz')
    
    // try new "emit" listener event ?
    MyEmitter.emit('xyz', 'bu bu bu') // nothing ;)
    
    // fire a "once" ? Yes, fire
    MyEmitter.once('abc', data => {
        console.log('fired by "once": ', data)
    })
    
    // run
    MyEmitter.emit('abc', 'Hello World') // its show listener only once
    
    // test "once" again
    MyEmitter.emit('abc', 'Hello World') // nothing 

提交回复
热议问题