订阅-发布

event bus 既是 node 中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计 模式,是非常重要的基础

  1. // 将下列代码补充完整,可以使代码正确执行
  2. class EventEmeitter {
  3. constructor() {
  4. this._events = this._events || new Map();
  5. }
  6. }
  7. EventEmeitter.prototype.emit = function( type, ...args ) {
  8. // your code
  9. }
  10. EventEmeitter.prototype.addListener = function(type, fn) {
  11. // your code
  12. }
  13. EventEmeitter.prototype.removeListener = function(type, fn) {
  14. // your code
  15. }
  16. const emitter = new EventEmeitter();
  17. const say = name => {
  18. console. log(`say: ${name}`);
  19. }
  20. emitter.addListener('say', say)
  21. emitter.addListener('say', say);
  22. emitter.emit('say', 'Hello World!');
  23. emitter.removeListener('say', say);
  24. emitter.emit('say', 'Hello World!');
  25. // say: Hello World!
  26. // say: Hello World!

实现

  1. // 代码实现
  2. class EventEmeitter {
  3. constructor() {
  4. // 选择了 Map 作为存储事件的结构,因为作为键值对的储存方式 Map 比一般对象更加适合,我们操作起来也更加简洁
  5. this._events = this._events || new Map(); // 储存事件,回调键值对
  6. this._maxListeners || 10; // 设置监听上限
  7. }
  8. }
  9. // 触发名为 type 的事件
  10. EventEmeitter.prototype.emit = function(type, ...args) {
  11. let handler = this._events.get(type);
  12. if (Array.isArray(handler)) {
  13. // 如果是一个数组说明有多个监听者,需要依次触发;里面的函数
  14. for (let i = 0; i < handler.length; i++) {
  15. /* 触发监听函数我们可以用 apply 与 call 两种方法,在少数参数时 call 的性能更好,
  16. * 多个参数时 apply 性能更好,当年 Node 的 Event 模块就在三个参数以下用 call 否则用 apply。
  17. * Node 全面拥抱 ES6+ 之后,相应的 call/apply 操作用 Reflect 新关键字重写了
  18. */
  19. if (args.length > 0) {
  20. handler[i].apply(this, args);
  21. } else {
  22. handler[i].call(this);
  23. }
  24. }
  25. } else if (handler && typeof handler === 'function') {
  26. // 单个函数情况直接触发
  27. if (args.length > 0) {
  28. handler.apply(this, args);
  29. } else {
  30. handler.call(this);
  31. }
  32. }
  33. // 返回自身,链式调用
  34. return this;
  35. }
  36. // 监听名为 type 的事件
  37. EventEmeitter.prototype.addListener = function(type, fn) {
  38. // 获取对应事件的函数清单
  39. const handler = this._events.get(type);
  40. if (!handler) {
  41. this._events.set(type, fn);
  42. } else if (handler && typeof handler === 'function') {
  43. // 如果 handler 是个函数,说明是一个监听者
  44. this._events.set(type, [handler, fn]); // 多个监听者需要用数组存储
  45. } else{
  46. // 存在同类型监听者,直接往数组添加
  47. handler.push(fn);
  48. }
  49. }
  50. // 移除名为 type 的事件
  51. EventEmeitter.prototype.removeListener = function(type, fn) {
  52. // 获取对应事件的函数清单
  53. const handler = this._events.get(type);
  54. if (handler && typeof handler === 'function') {
  55. // 如果 handler 是个函数,说明只被监听一次
  56. this._events.delete(type, fn);
  57. } else{
  58. if (!Array.isArray(handler)) {
  59. return this;
  60. }
  61. // 如果 handler 是数组,说明被监听多次,需要依次移除对应的函数
  62. let position = handler.indexOf(fn);
  63. // 如果找到匹配的函数,从数组清除
  64. if (position !== -1) {
  65. // 找到对应的位置,直接清除此回调
  66. handler.splice(position, 1);
  67. // 如果清除后只有一个函数,那么取消数组,以函数形式保存
  68. if (handler.length === 1) {
  69. this._events.set(type, handler[0]);
  70. }
  71. }
  72. }
  73. }