一、问题描述
实现一个EventEmitter类,这个类包含以下方法:
on(监听事件,该事件可以被触发多次)
once(也是监听事件,但只能被触发一次)
fire(触发指定的事件)
off(移除指定事件的某个回调方法或者所有回调方法)

class EventEmitter {
/**请补充你的代码***/
}
const event = new EventEmitter()
const drink = (person) => {
console.log(person + '喝水')
}
event.on('drink', drink)
event.on('eat', (person) => {
console.log(person + '吃东西')
})
event.once('buy', (person) => {
console.log(person + '买东西')
})
event.fire('drink', '我') // 我喝水
event.fire('drink', '我') // 我喝水
event.fire('eat', '其它人') // 其它人吃东西
event.fire('eat', '其它人') // 其它人吃东西
event.fire('buy', '其它人') //其它人买东西
event.fire('buy', '其它人') //这里不会再次触发buy事件,因为once只能触发一次
event.off('eat') //移除eat事件
event.fire('eat', '其它人') //这里不会触发eat事件,因为已经移除了

二、问题分析
这题其实就是实现发布-订阅模式了,难点在于怎样实现once事件,即只触发一次。其实也就是要实现两种类型的事件,我们可以用不同的对象去保存这两种类型的事件,然后在fire的时候,这两种事件都要被处理即可。

三、参考链接

https://github.com/Olical/EventEmitter

四、解决方案

class EventEmitter {
constructor() {
this.queue = {} //可触发多次的事件
this.onceQueue = {} //只能触发一次的事件
}
on(event, fn) { //监听事件,可以触发多次
if (!this.queue[event]) this.queue[event] = []
this.queue[event].push(fn)
}
once(event, fn) { //监听事件,只能触发一次
if (!this.onceQueue[event]) {
this.onceQueue[event] = {
fns: [],
hasFired: false
}
}
this.onceQueue[event].fns.push(fn)
}
fire() { //触发指定的事件
const event = [].shift.call(arguments), //取得事件名称
fns = this.queue[event], //取得该事件里所有的回调函数(可以触发多次的事件)
onceFns = this.onceQueue[event] //取得该事件里所有的回调函数(只能触发一次的事件)
if (fns && fns.length != 0) {
for (let item of fns) {
item.apply(this, arguments)
}
}
if (onceFns && !onceFns.hasFired) {
for (let item of onceFns.fns) {
item.apply(this, arguments)
}
this.onceQueue[event].hasFired = true
}
}
off(event, fn = null) { //可移除特定事件里的某个回调函数或者所有回调函数
const fns = this.queue[event]
if (!fns || fns.length == 0) return
if (fn) { //移除该事件特定的回调
this.queue[event] = fns.filter(item => {
return item !== fn
})
} else { //移除该事件所有的回调
this.queue[event] = []
}
}
}

观察者模式之ES6实现(二)的更多相关文章

  1. React和ES6(二)ES6的类和ES7的property initializer

    React与ES6系列: React与ES6(一)开篇介绍 React和ES6(二)ES6的类和ES7的property initializer React与ES6(三)ES6类和方法绑定 React ...

  2. ES6入门(二)

    目录 ES6入门(二) es6之解构赋值 数组的解构赋值 对象的解构赋值 字符串的解构赋值 数值和布尔值的解构赋值 函数参数的解构赋值 圆括号问题 ES6入门(二) es6之解构赋值 数组的解构赋值 ...

  3. 用vue.js学习es6(二):let和const使用

    一.运行及关闭运行: 在上一节中我们用shift+右击在C:\vue\es6文件夹中打开命令行使用:npm run dev,打开了我们的vue界面. 如果要关闭则在命令行中按住ctrl+C则可以关闭. ...

  4. 委托、事件、Observer观察者模式的使用解析二

    一.设计模式-Observer观察者模式 Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新.Observer模式是一种 ...

  5. 深入浅出ES6(二):迭代器和for-of循环

    作者 Jason Orendorff  github主页  https://github.com/jorendorff 我们如何遍历数组中的元素?20年前JavaScript刚萌生时,你可能这样实现数 ...

  6. JavaScript学习笔记 -- ES6学习(二) let 和const

    ES6 中新增了两个命令: let 和const. let命令: let 用于声明变量,和var 类似,但是所声明的变量只在代码块中有效,不存在变量提升,有暂时性死区. 1.只在代码块中有效 和var ...

  7. ES6核心内容精讲--快速实践ES6(二)

    Iterator和for...of 是什么: Iterator(遍历器)是专门用来控制如何遍历的对象,具有特殊的接口. 怎么用: Iterator(遍历器)对象带有next方法,每一次调用next方法 ...

  8. es6(二)

    三 . 正则扩展: 1.构造函数的扩展 let regex = new Regex('xyz','i'); let regex2 = new Regex(/xyz/i);//test() 方法用于检测 ...

  9. 从零开始学 Web 之 ES6(二)ES5的一些扩展

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

随机推荐

  1. move_base

    1>准备导航所需要的包. a.ros-indigo-gampping :我们不需要修改包内的东西,所以直接安装可执行文件就好了. sudo apt-get install ros-indigo- ...

  2. Dubbo多版本

    当服务提供者提供的服务接口出现不兼容升级时,可以设置版本号,使用多个版本号(version)进行过渡. 1).服务提供者配置文件 <dubbo:service ref="userSer ...

  3. UML和模式应用4:初始阶段(5)--用例编写的准则

    1.前言 本文主要介绍用例编写时所遵循的几条基本准则. 2.用例编写的准则 2.1 以本质的风格编写用例 如系统认证,而不要说 需要输入ID进行认证等 2.2 编写简洁的用例 如系统认证,不要说 这个 ...

  4. linux添加定时任务crond

    1.crontab –e:编辑当前定时任务 保存完重新crond : service crond restart 2. crontab用法 crontab –e : 修改 crontab 文件,如果文 ...

  5. Python3学习笔记25-logging模块

    logging模块,Python自带用来记录日志的模块. 因为工作需要用到关于日志的,最近一直都在看关于日志模块的东西,百度了很多文章,可惜都是看的让人一头雾水,最后运气不错,找到一篇很详细的文章.传 ...

  6. Linux清除文件内容的几种方法

    # 清空或删除大文件内容的五种方法: # 法一:通过重定向到 Null 来清空文件内容 $ >test.sh # 法二:使用 ‘true' 命令重定向来清空文件 $ true > test ...

  7. Linux常用命令3(压缩和解压缩总结)

    tar命令 解包:tar zxvf FileName.tar 打包:tar czvf FileName.tar DirName gz命令 解压1:gunzip FileName.gz 解压2:gzip ...

  8. 服务发现之consul的介绍、部署和使用

    什么是服务发现 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是我们的重点.我们看下面的一幅图片:     图中 ...

  9. 在vue-cli中引用公共过滤器filter

    在实际项目开发中,在某一组件中声明的全局过滤器Vue.filter并不能在其他组件中使用,所以,我认为只要调用两次以上或者可能会被调用两次以上的过滤器,就应该写入统一个过滤器文件中,方便统一调用.下面 ...

  10. Windows环境selenium+Python环境配置

    1.安装Python 访问Python官方网站. 根据自己的操作系统32/64 位,选择相应的版本. 安装过程我就不详细描述了,动动手指头,Google一下,你就知道.我的安装目录为:C:\Pytho ...