实现思路:

    1. 创建一个对象
    2. 在该对象上创建一个缓存列表(调度中心)
    3. on 方法用来把函数 fn 都加到缓存列表当中(订阅者注册事件到调度中心)
    4. emit 方法取到 arguments 里第一个当作 event ,根据 event 值取执行对象缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
    5. off 方法可以根据 event 值取消订阅(取消订阅)
    6. once 方法只监听一次,调用完毕后删除缓存函数(订阅一次)

    下面的例子实现了 on 和 emit 方法:

    1. // 公众号对象
    2. let eventEmitter = {};
    3. // 缓存列表,存放 event 及 fn
    4. eventEmitter.list = {};
    5. // 订阅
    6. eventEmitter.on = function (event, fn) {
    7. let _this = this;
    8. // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
    9. // 如果对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
    10. (_this.list[event] || (_this.list[event] = [])).push(fn);
    11. return _this;
    12. };
    13. // 发布
    14. eventEmitter.emit = function () {
    15. let _this = this;
    16. // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
    17. let event = [].shift.call(arguments);
    18. let fns = [..._this.list[event]];
    19. // 如果缓存列表里没有 fn 就返回 false
    20. if (!fns || fns.length === 0) {
    21. return false;
    22. };
    23. // 遍历 event 值对应的缓存列表,依次执行 fn
    24. fns.forEach(fn => {
    25. fn.apply(_this, arguments);
    26. });
    27. return _this;
    28. };
    29. function user1(content) {
    30. console.log("用户1订阅了: ", content);
    31. };
    32. function user2(content) {
    33. console.log("用户2订阅了: ", content);
    34. };
    35. // 订阅
    36. eventEmitter.on("article", user1);
    37. eventEmitter.on("article", user2);
    38. // eventEmitter.emit("article", "JavaScript 发布-订阅模式");
    39. /*
    40. 用户1订阅了: JavaScript 发布-订阅模式
    41. 用户2订阅了: JavaScript 发布-订阅模式
    42. */

    下面补充一下 once 和 off 方法:

    1. let eventEmitter = {
    2. // 缓存列表
    3. list: {},
    4. // 订阅
    5. on (event, fn) {
    6. let _this = this;
    7. // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
    8. // 如果对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
    9. (_this.list[event] || (_this.list[event] = [])).push(fn);
    10. return _this;
    11. },
    12. // 监听一次
    13. once (event, fn) {
    14. // 先绑定,调用后删除
    15. let _this = this;
    16. function on() {
    17. _this.off(event, on);
    18. fn.apply(_this, arguments);
    19. }
    20. on.fn = fn;
    21. _this.on(event, on);
    22. return _this;
    23. };
    24. // 取消订阅
    25. off (event, fn) {
    26. let _this = this;
    27. let fns = _this.list[event];
    28. // 如果缓存列表中没有相应的fn,返回false
    29. if(!fns) return false;
    30. if (!fn) {
    31. // 如果没有传 fn 的话,就会将 event 值对象缓存列表中的 fn 都清空
    32. fns && (fns.length = 0);
    33. } else {
    34. // 若有 fn ,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
    35. let cb;
    36. fot (let i = 0, cbLen = fns.length; i < cbLen; i++) {
    37. cb = fns[i];
    38. if (cb === fn || cb.fn === fn) {
    39. fns.splice(i, 1);
    40. break;
    41. }
    42. }
    43. }
    44. return _this;
    45. },
    46. // 发布
    47. emit() {
    48. let _this = this;
    49. // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
    50. let event = [].shift.call(arguments);
    51. let fns = [..._this.list[event]];
    52. // 如果缓存列表里没有 fn 就返回 false
    53. if (!fns || fns.length === 0) {
    54. return false;
    55. };
    56. // 遍历 event 值对应的缓存列表,依次执行 fn
    57. fns.forEach(fn => {
    58. fn.apply(_this, arguments);
    59. });
    60. return _this;
    61. }
    62. };
    63. function user1(content) {
    64. console.log("用户1订阅了: ", content);
    65. };
    66. function user2(content) {
    67. console.log("用户2订阅了: ", content);
    68. };
    69. function user3(content) {
    70. console.log("用户3订阅了: ", content);
    71. };
    72. function user4(content) {
    73. console.log("用户4订阅了: ", content);
    74. };
    75. // 订阅
    76. eventEmitter.on("article1", user1);
    77. eventEmitter.on("article1", user2);
    78. eventEmitter.on("article1", user3);
    79. // 取消user2方法的订阅
    80. eventEmitter.off('article1', user2);
    81. eventEmitter.once('article2', user4);
    82. // 发布
    83. eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
    84. eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
    85. eventEmitter.emit('article2', 'Javascript 观察者模式');
    86. eventEmitter.emit('article2', 'Javascript 观察者模式');
    87. // eventEmitter.on('article1', user3).emit('article1', 'test111');
    88. /*
    89. 用户1订阅了: Javascript 发布-订阅模式
    90. 用户3订阅了: Javascript 发布-订阅模式
    91. 用户1订阅了: Javascript 发布-订阅模式
    92. 用户3订阅了: Javascript 发布-订阅模式
    93. 用户4订阅了: Javascript 观察者模式
    94. */

    参考文章: