发布订阅
一个对象满足监听、触发、移除的方法,我们便称这个对象实现了发布订阅模式。所有的异步任务都可以通过发布订阅来处理,是一个通用的异步任务管理方案。
// 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'))
发布订阅也有缺点:事件过多时难管理,字符串容易拼错