Skip to content

https://juejin.im/post/5b125ad3e51d450688133f22

正好学习时看到这篇文章,不知是不是小伙伴写的,值得学习借鉴,就挪过来了

自定义事件 模子

js
let corp = {} // 自定义一个公司对象
corp.list = [] // 这里放一个列表用来缓存回调函数

// 订阅事件
corp.on = function(fn) {
  this.list.push(fn)
}

// 发布事件
corp.emit = function() {
  // 当发布时再把列表里存的函数依次执行
  this.list.forEach(cb => {
    cb.apply(this, arguments)
  })
}

// 测试用例
corp.on((position, salary) => {
  console.log(`职位:${position}, 薪水:${salary}`)
})
corp.on((skill, hobby) => {
  console.log(`技能: ${skill}, 爱好: ${hobby}`)
})
corp.emit('前端', '10k')
corp.emit('端茶倒水', '把妹')

上面这样写后, corp.emit 一次性把之前的信息都打印出来了,我们做点处理

js
let corp = {}
corp.list = {}

corp.on = function(key, fn) {
  // 如果对象中没有对应key值,也就说明没有订阅过
  // 那就给key创建个缓存列表
  if (!this.list[key]) this.list[key] = []

  // 把函数添加到对应key的缓存列表中
  this.list[key].push(fn)
}

corp.emit = function() {
  // 第一个参数是对应的key值
  // 直接用数组的shift方法取出
  let key = [].shift.call(arguments)
  let fns = this.list[key]
  // 如果缓存列表里没有函数就返回false
  if (!fns || fns.length === 0) return false

  // 遍历key值对应的缓存列表,依次执行函数的方法
  fns.forEach(fn => fn.apply(this, arguments))
}

// 测试用例
corp.on('join', (position, salary) => {
    console.log('你的职位是:' + position);
    console.log('期望薪水:' + salary);
});
corp.on('other', (skill, hobby) => {
    console.log('你的技能有: ' + skill);
    console.log('爱好: ' + hobby);
});

corp.emit('join', '前端', 10000);
corp.emit('join', '后端', 10000);
corp.emit('other', '端茶和倒水', '足球');

实现一个通用的发布订阅模式

思路:

  1. 创建一个对象(缓存列表)
  2. on方法用来把回调函数fn都加到缓存列表中
  3. emit方法取到arguments里第一项当做key,根据key值去执行对应缓存列表中的函数
  4. remove方法可以根据key值取消订阅
js
class CustomEvent {
  constructor() {
    this.list = {}
  }

  on(key, fn) {
    if (!this.list[key]) this.list[key] = []
    this.list[key].push(fn)
  }

  emit() {
    let key = [].shift.call(arguments)
    let fns = this.list[key]

    if (!fns || fns.length === 0) return false
    fns.forEach(fn => fn.apply(this, arguments))
  }

  off(key, fn) {
    // 取消订阅
    let fns = this.list[key]
    // 如果缓存列表中没有函数,返回false
    if (!fns) return false
    // 如果没有传对应函数,就会将key值对应缓存列表中的函数都清空掉
    if (!fn) {
      fns && (fns.length = 0)
    } else {
      // 遍历缓存列表,看看传入的fn与哪个函数相同
      // 如果相同就直接从缓存列表中删除即可
      fns.forEach((cb, i) => {
        if (cb === fn) fns.splice(i, 1)
      })
    }
  }
}

总结

优点:

  • 对象间的解耦
  • 异步编程中,可以更松耦合地代码编写

缺点:

  • 创建订阅者本身要消耗一定的时间和内存
  • 多个发布者和订阅者嵌套一起时,程序难以跟踪维护

共 20 个模块,1301 篇 Markdown 文档。