发布订阅——学习日志

63 阅读3分钟

发布订阅

一个对象满足监听、触发、移除的方法,我们便称这个对象实现了发布订阅模式。所有的异步任务都可以通过发布订阅来处理,是一个通用的异步任务管理方案。

 

// const button = document.getElementById('x')

// const fn = () => console.log('hi')

// button.addEventListener('click',fn)   //订阅

// setTimeout(() => {

//     // button.click()       //发布

//     button.dispatchEvent(new Event('click'))    //常用发布方式

// },3000)

 

//自己设计一个发布订阅模式,首先不考虑事件名

//首先用闭包实现

// const eventEmitter = () => {

//     const array = []

//     return {

//         addEventListener: (fn) => {

//             array.push(fn)

//         },

//         dispatchEvent: (event) => {

//             array.forEach(fn => fn.call(null, event))

//         },

//         removeEventListener: (fn) => {

//             const index = array.indexOf(fn)

//             array.splice(index, 1)

//         }

//     }

// }

// const ajax = eventEmitter()

// const f1 = () => console.log(1)

// const f2 = () => console.log(2)

// const f3 = () => console.log(3)

// ajax.addEventListener(f1)

// ajax.addEventListener(f2)

// ajax.addEventListener(f3)

// ajax.removeEventListener(f2)

// ajax.dispatchEvent(new Event('click'))    //直接ajax.dispatchEvent()这样都可以,现在还没有考虑事件名

 

//用class实现

// class EventEmitter {

//     constructor(){

//         this.array = []

//     }

//     addEventListener(fn){

//         this.array.push(fn)

//     }

//     dispatchEvent (event) {

//         this.array.forEach(fn => fn.call(null, event))

//     }

//     removeEventListener(fn) {

//         const index = this.array.indexOf(fn)

//         this.array.splice(index, 1)

//     }

// }

// const ajax = new EventEmitter()

// const f1 = () => console.log(1)

// const f2 = () => console.log(2)

// ajax.addEventListener(f1)

// ajax.addEventListener(f2)

// ajax.removeEventListener(f1)

// ajax.dispatchEvent(new Event('click'))

 

//使用构造函数+原型实现

// function EventEmitter() {

//     if(!(this instanceof EventEmitter)) {

//         return new EventEmitter

//     }

//     this.array = []

// }

// EventEmitter.prototype = {

//     constructor: EventEmitter,

//     addEventListener(fn){

//         this.array.push(fn)

//     },

//     dispatchEvent (event) {

//         this.array.forEach(fn => fn.call(null, event))

//     },

//     removeEventListener(fn) {

//         const index = this.array.indexOf(fn)

//         this.array.splice(index, 1)

//     }

// }

// const ajax = new EventEmitter()

// const f1 = () => console.log(1)

// const f2 = () => console.log(2)

// ajax.addEventListener(f1)

// ajax.addEventListener(f2)

// ajax.removeEventListener(f1)

// ajax.dispatchEvent(new Event('click'))

//以上都是不考虑事件名,现在要求考虑事件名了

 

//解决方案,以上面第一个版本做修改

// const eventEmitter = () => {

//     const hash = {

//       // 'click': [],

//       // 'mouseover': [],

//     }

//     return {

//         addEventListener: (eventName, fn) => {

//             hash[eventName] = hash[eventName] || []    //如果hash[eventName]有值,那就自己等于自己,没有的话就为空数组[]

//             hash[eventName].push(fn)

//         },

//         dispatchEvent: (eventName, event) => {

//             hash[eventName]?.forEach(fn => fn.call(null, event))

//         },

//         removeEventListener: (eventName, fn) => {

//             if(hash[eventName] === undefined) return

//             const index = hash[eventName].indexOf(fn)

//             if(index < 0) return

//             hash[eventName].splice(index, 1)

//         }

//     }

// }

// const ajax = eventEmitter()

// const f1 = () => console.log(1)

// const f2 = () => console.log(2)

// ajax.addEventListener('click', f1)

// ajax.addEventListener('click', f2)

// ajax.addEventListener('mouseover', ()=>{

//     console.log('mouseover')

// })

// ajax.removeEventListener('click', f1)

// ajax.dispatchEvent('click', new Event('xxx'))

// ajax.dispatchEvent('mouseover', new Event('xxx'))

//现要求考虑fn的参数

 

//解决方案

// const eventEmitter = () => {

//     const hash = {}

//     return {

//         addEventListener: (eventName, fn) => {

//             hash[eventName] = hash[eventName] || []

//             hash[eventName].push(fn)

//         },

//         dispatchEvent: (eventName, ...dataList) => {

//             hash[eventName]?.forEach(fn => fn.apply(null, dataList))

//         },

//         removeEventListener: (eventName, fn) => {

//             if(hash[eventName] === undefined) return

//             const index = hash[eventName].indexOf(fn)

//             if(index < 0) return

//             hash[eventName].splice(index, 1)

//         }

//     }

// }

// const ajax = eventEmitter()

// const f1 = (a, b) => console.log('f1', a, b)

// const f2 = (a, b) => console.log('f2', a, b)

// ajax.addEventListener('click', f1)

// ajax.addEventListener('click', f2)

// ajax.addEventListener('mouseover', (a, b)=>{

//     console.log('mouseover', a, b)

// })

// ajax.removeEventListener('click', f1)

// ajax.dispatchEvent('click', {name: 'frank'}, {name: 'jack'})

// ajax.dispatchEvent('mouseover', new Event('xxx'))

//新要求:不单独创建对象

 

//解决方案

// const eventEmitter = () => {

//     const hash = {}

//     return {

//         addEventListener: (eventName, fn) => {

//             hash[eventName] = hash[eventName] || []

//             hash[eventName].push(fn)

//         },

//         dispatchEvent: (eventName, ...dataList) => {

//             hash[eventName]?.forEach(fn => fn.apply(null, dataList))

//         },

//         removeEventListener: (eventName, fn) => {

//             if(hash[eventName] === undefined) return

//             const index = hash[eventName].indexOf(fn)

//             if(index < 0) return

//             hash[eventName].splice(index, 1)

//         }

//     }

// }

// Object.prototype.addEventListener = function (eventName, fn) {

//     this.hash = this.hash || {}

//     this.hash[eventName] = this.hash[eventName] || []

//     this.hash[eventName].push(fn)

// }

// Object.prototype.removeEventListener = function (eventName, fn) {

//     if(this.hash === undefined) return

//     if(this.hash[eventName] === undefined) return

//     const index = this.hash[eventName].indexOf(fn)

//     if(index < 0) return

//     this.hash[eventName].splice(index, 1)

// }

// Object.prototype.dispatchEvent = function (eventName, ...dataList) {

//     this.hash[eventName]?.forEach(fn => fn.apply(null, dataList))

// }

// const ajax = {}

// const f1 = (a, b) => console.log('f1', a, b)

// const f2 = (a, b) => console.log('f2', a, b)

// ajax.addEventListener('click', f1)

// ajax.addEventListener('click', f2)

// ajax.addEventListener('mouseover', (a, b)=>{

//     console.log('mouseover', a, b)

// })

// ajax.removeEventListener('click', f1)

// ajax.dispatchEvent('click', {name: 'frank'}, {name: 'jack'})

// ajax.dispatchEvent('mouseover', new Event('xxx'))

//改成功了,但这种方法不推荐,不做记忆要求

//既然什么对象都可以,那么函数对象应该也行

 

//解决方案:用AJAX

const eventEmitter = () => {

    const hash = {}

    return {

        addEventListener: (eventName, fn) => {

            hash[eventName] = hash[eventName] || []

            hash[eventName].push(fn)

        },

        dispatchEvent: (eventName, ...dataList) => {

            hash[eventName]?.forEach(fn => fn.apply(null, dataList))

        },

        removeEventListener: (eventName, fn) => {

            if(hash[eventName] === undefined) return

            const index = hash[eventName].indexOf(fn)

            if(index < 0) return

            hash[eventName].splice(index, 1)

        }

    }

}

Function.prototype.addEventListener = function (eventName, fn) {

    this.hash = this.hash || {}

    this.hash[eventName] = this.hash[eventName] || []

    this.hash[eventName].push(fn)

}

Function.prototype.removeEventListener = function (eventName, fn) {

    if(this.hash === undefined) return

    if(this.hash[eventName] === undefined) return

    const index = this.hash[eventName].indexOf(fn)

    if(index < 0) return

    this.hash[eventName].splice(index, 1)

}

Function.prototype.dispatchEvent = function (eventName, ...dataList) {

    this.hash[eventName]?.forEach(fn => fn.apply(null, dataList))

}

const ajax = function(method, url, options){

    const {data} = options

    const xhr = new XMLHttpRequest()

    xhr.open(method, url)

    xhr.onreadystatechange = function(){

        if (xhr.readyState === 4) {  //下载成功

            if (xhr.status >= 200 && xhr.status <300) {   //调用后端接口成功

                ajax.dispatchEvent('success',xhr.responseText, xhr)

            } else {    //调用后端接口失败

                ajax.dispatchEvent('fail', xhr)

            }

        }

    }

    xhr.send(data)

}

const f1 = (a, b) => console.log('f1', a, b)

const f2 = (a, b) => console.log('f2', a, b)

ajax.addEventListener('success', f1)

ajax.addEventListener('fail', f2)

ajax.removeEventListener('success', f1)

ajax.dispatchEvent('fail', {name: 'frank'}, {name: 'jack'})

ajax.dispatchEvent('fail', new Event('xxx'))

 

发布订阅也有缺点:事件过多时难管理,字符串容易拼错