JS手写 发布订阅

发布订阅

发布订阅是一种设计模式。
发布订阅:
一个对象能够发布一个事件,然后你能订阅这个事件。
设计模式:
是一种可用的、别人用过觉得还行的一种写代码套路,比如用闭包去隐藏变量。
需求:
两个模块间形成事件交互。
模块A和B ,中间值e叫做事件中心。
A有什么事通知B就触发一下e,B就订阅一下。
B有什么事通知A就出发一下e,A就订阅一下
两者不直接交流,由e牵线搭桥。

分析

我们需要实现一个对象,这个对象就是通信的中介eventHub,实现三个接口。
eventHub这个对象要有三个函数
const eventHub = {
  on:()=>{} //监听事件
  emit:()=>{} //trigger 触发事件
  off:()=>{} //取消监听事件
}
//在任意地方使用on来监听事件
eventHub.on('click',f1)
//用off取消监听事件
eventHub.off('click',f1)
//可以在用户点击按钮 或三秒钟之后触发它
setTimeout(()=>{
  eventHub.emit('click','frank')
},3000)

实现

写函数首先搞清楚输入输出是什么
on 接收两个参数 事件类型和函数,不需要返回值。
off同理。
emit接收事件类型和参数。依旧不需要返回值
它们分别做什么
on:如果你监听了一个事件,我就把你放到任务队列中。
off; 就是把它从任务队列中拿出来。
emit就是调用队列中的任务。
任务队列是什么

  • 任务:函数就是任务。
  • 队列:
    需要在触发时执行这个任务,比如我监听两次click。
    eventHub.on('click',f1)
    eventHub.on('click',f2)
    那我f1 和 f2调用的顺序,先调用f1再调用f2。
    先进先出就是队列,所以我们需要一个队列。

但是实际上我们会有很多队列:如click队列,右键队列 左键多列等等。
所以我们要实现一个映射关系,用map 叫哈希表。JS中只能用对象来表示哈希表。
所以我们实现一个map队列表。

用到的其他东西

  • 防御性编程:如果name不存在我们可以初始化一个。
  on:(name,fn)=>{
    写法1:直接push
    eventHub.map[name].push(fn)可以 但是需要防御性编程 name有可能是空的
    写法2:防御性编程
    if(eventHub.map[name]===undefined){
      eventHub.map[name] = []
    } 
    eventHub.map[name].push(fn)
    写法3:简写
    eventHub.map[name] = eventHub.map[name] || [] 
    eventHub.map[name].push(fn)
//这里不能乱用别名,因为另外函数是这个是写。q = []就出现问题了
}
  • 短路:可以少写else
  if(!q){return}
  if(index<0) {return} 
  • 别名:减少代码。alias设计模式,叫缩写也叫别名。
const q = eventHub.map[name]
  • forEach和map:
    用map和forEach遍历基本一样 但是map有返回值 forEach没有
  • 代码
const eventHub = {
  map:{
     
  },
  on:(name,fn)=>{
    //入队
    eventHub.map[name] = eventHub.map[name] || [] 
    eventHub.map[name].push(fn)
  } ,
  emit:(name,data)=>{
    const q = eventHub.map[name]
    if(!q){return} 
    q.map((f)=>{f.call(null,data)})//遍历调用一遍。 这f的this是空。
   } ,
  off:(name,fn)=>{
    const q = eventHub.map[name]
    if(!q){return}
    const index = q.indexOf(fn)
    if(index<0) {return} 
    q.splice(index,1)
  }
}
  • 测试
eventHub.on('click',console.log)
eventHub.on('click',console.error)
setTimeout(()=>{
  eventHub.emit('click','frank')
},3000)
  • 完美


    发布订阅

    面试有可能会问到 如何实现once:()=>{}
    只监听一次,自动结束。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.js当中有哪些数据类型 5个基础:字符串,布尔,数值,null,undefined,1个复杂:Object在e...
    林不羁吖阅读 271评论 0 0
  • 、 、 在相应情况下可省略。而 是必须写的。 除了 、 其它标签都有默认样式。 ★可以出现在 元素内的标签有 、 ...
    HeroMeikong阅读 353评论 0 0
  • 方便起见不分parameter[形参,出现在函数定义中]和argument[实参,其值为传入函数的值],一律当作a...
    东月三二阅读 403评论 0 0
  • 前言 以前我看到面试贴就直接刷掉的,从不会多看一眼,直到去年 9 月份我开始准备面试时,才发现很多面试经验贴特别有...
    93ac81ebff1e阅读 140评论 0 0
  • 本节介绍 JavaScript 语言实际编程中,涉及面向对象编程的一些模式。 1.构造函数的继承 让一个构造函数继...
    wit92阅读 270评论 0 0