实现思路:
- 创建一个对象
- 在该对象上创建一个缓存列表(调度中心)
- on 方法用来把函数 fn 都加到缓存列表当中(订阅者注册事件到调度中心)
- emit 方法取到 arguments 里第一个当作 event ,根据 event 值取执行对象缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
- off 方法可以根据 event 值取消订阅(取消订阅)
- once 方法只监听一次,调用完毕后删除缓存函数(订阅一次)
下面的例子实现了 on 和 emit 方法:
// 公众号对象let eventEmitter = {};// 缓存列表,存放 event 及 fneventEmitter.list = {};// 订阅eventEmitter.on = function (event, fn) {let _this = this;// 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表// 如果对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里(_this.list[event] || (_this.list[event] = [])).push(fn);return _this;};// 发布eventEmitter.emit = function () {let _this = this;// 第一个参数是对应的 event 值,直接用数组的 shift 方法取出let event = [].shift.call(arguments);let fns = [..._this.list[event]];// 如果缓存列表里没有 fn 就返回 falseif (!fns || fns.length === 0) {return false;};// 遍历 event 值对应的缓存列表,依次执行 fnfns.forEach(fn => {fn.apply(_this, arguments);});return _this;};function user1(content) {console.log("用户1订阅了: ", content);};function user2(content) {console.log("用户2订阅了: ", content);};// 订阅eventEmitter.on("article", user1);eventEmitter.on("article", user2);// eventEmitter.emit("article", "JavaScript 发布-订阅模式");/*用户1订阅了: JavaScript 发布-订阅模式用户2订阅了: JavaScript 发布-订阅模式*/
下面补充一下 once 和 off 方法:
let eventEmitter = {// 缓存列表list: {},// 订阅on (event, fn) {let _this = this;// 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表// 如果对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里(_this.list[event] || (_this.list[event] = [])).push(fn);return _this;},// 监听一次once (event, fn) {// 先绑定,调用后删除let _this = this;function on() {_this.off(event, on);fn.apply(_this, arguments);}on.fn = fn;_this.on(event, on);return _this;};// 取消订阅off (event, fn) {let _this = this;let fns = _this.list[event];// 如果缓存列表中没有相应的fn,返回falseif(!fns) return false;if (!fn) {// 如果没有传 fn 的话,就会将 event 值对象缓存列表中的 fn 都清空fns && (fns.length = 0);} else {// 若有 fn ,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可let cb;fot (let i = 0, cbLen = fns.length; i < cbLen; i++) {cb = fns[i];if (cb === fn || cb.fn === fn) {fns.splice(i, 1);break;}}}return _this;},// 发布emit() {let _this = this;// 第一个参数是对应的 event 值,直接用数组的 shift 方法取出let event = [].shift.call(arguments);let fns = [..._this.list[event]];// 如果缓存列表里没有 fn 就返回 falseif (!fns || fns.length === 0) {return false;};// 遍历 event 值对应的缓存列表,依次执行 fnfns.forEach(fn => {fn.apply(_this, arguments);});return _this;}};function user1(content) {console.log("用户1订阅了: ", content);};function user2(content) {console.log("用户2订阅了: ", content);};function user3(content) {console.log("用户3订阅了: ", content);};function user4(content) {console.log("用户4订阅了: ", content);};// 订阅eventEmitter.on("article1", user1);eventEmitter.on("article1", user2);eventEmitter.on("article1", user3);// 取消user2方法的订阅eventEmitter.off('article1', user2);eventEmitter.once('article2', user4);// 发布eventEmitter.emit('article1', 'Javascript 发布-订阅模式');eventEmitter.emit('article1', 'Javascript 发布-订阅模式');eventEmitter.emit('article2', 'Javascript 观察者模式');eventEmitter.emit('article2', 'Javascript 观察者模式');// eventEmitter.on('article1', user3).emit('article1', 'test111');/*用户1订阅了: Javascript 发布-订阅模式用户3订阅了: Javascript 发布-订阅模式用户1订阅了: Javascript 发布-订阅模式用户3订阅了: Javascript 发布-订阅模式用户4订阅了: Javascript 观察者模式*/
参考文章:
