发布订阅
很多人javascript设计模式看了n遍,从不太理解到理解,再到每次看都了然于胸一天之后就忘得干干净净,其实应用起来或者写下来,也许你会理解得更彻底一些;
上代码吧
let event = { // event 事件对象 list: {}, // event绑定的事件订阅列表 on(key, fn) { // 添加事件绑定 key表示要添加绑定的事件类型 if (!this.list[key]) { // 如果没有则创建空数组 this.list[key] = []; } this.list[key].push(fn); // push进该类型事件数组 }, emit() { let key = [].shift.call(arguments), //发布 取key值为emit的第一个参数 fns = this.list[key]; if (!fns || fns.length === 0) { // 如果该事件订阅列表为空 返回false 这里不进行其他处理 return false; } // 如果该事件订阅列表存在,则依次执行 fns.forEach(fn => { fn.apply(this, arguments); // 这里的arguments已经删除了key值 }); }, remove(key, fn) { // 取消订阅 let fns = this.list[key]; if (!fns) return false; // 如果缓存列表中没有函数,返回false if (!fn) { // 如果没有传对应函数的话 就将key值对应缓存列表中的函数都清空掉 fns && (fns.length = 0); } else { // 遍历缓存列表,看看传入的fn与哪个函数相同,如果相同就直接从缓存列表中删掉即可 fns.forEach((cb, i) => { if (cb === fn) { fns.splice(i, 1); } }); } }};// 测试function cat(data) { console.log(data, '喵喵喵');}function dog(data) { console.log(data, '旺旺旺');}function animal(data) { console.log(data, '都是主子');}function pet(data) { console.log(data, '都是动物');}event.on('animal',animal);event.on('animal', pet);event.on('cat', cat);event.on('dog', dog);// 发布event.emit('animal', ['藏獒', '喵星人']);event.emit('cat', ['喵星人']);event.emit('dog', ['藏獒']);// 取消dog方法的订阅event.remove('animal', pet);// 再次发布event.emit('animal', ['藏獒', '喵星人']);复制代码
很清晰了,接下来再聊一聊node的一个模块events吧
node 的 events模块的应用
// 监听的目的 就是为了构造这样一个对象 一对多的关系 on// 发布的时候 会让数组的函数依次执行 emitlet EventEmitter = require('events'); // 引入events模块class Girl extends EventEmitter{} // class继承let girl = new Girl(); // new 一个Girl的实例let shopping = function (data) { // 购物 console.log(data, '去购物');};let dieting = function (data) { // 节食 console.log(data, '去节食');};girl.on('newListener', function (eventName) { // 添加订阅的监听 console.log('新添加了订阅: ' + eventName);});girl.setMaxListeners(3); // 设置最大订阅数console.log('最大订阅数是:', girl.getMaxListeners()); // 打印最大订阅数girl.on('减肥', dieting); // 订阅减肥 girl.once('发工资', shopping); // 订阅发工资girl.prependListener('发工资', function () { console.log('这是发工资前');});复制代码
当然这里是使用node的模块,那如果我想自己去实现一个这样的event模块呢
node 的 events模块的实现
上代码
function EventEmitter() { // this._events = {}; // 用Object.create(null)代替空对象{}的好处是无杂质,不继承原型链 this._events = Object.create(null); } // 默认最多的绑定次数 EventEmitter.defaultMaxListeners = 10; // 同on方法 EventEmitter.prototype.addListener = EventEmitter.prototype.on; // 返回监听的事件名 EventEmitter.prototype.eventNames = function () { return Object.keys(this._events); }; // 设置最大监听数 EventEmitter.prototype.setMaxListeners = function (n) { this._count = n; }; // 返回监听数 EventEmitter.prototype.getMaxListeners = function () { return this._count ? this._count : this.defaultMaxListeners; }; // 监听 EventEmitter.prototype.on = function (type, cb, flag) { // 默认值,如果没有_events的话,就给它创建一个 if (!this._events) { this._events = Object.create(null); } // 不是newListener 就应该让newListener执行以下 if (type !== 'newListener') { this._events['newListener'] && this._events['newListener'].forEach(listener => { listener(type); }); } if (this._events[type]) { // 根据传入的flag来决定是向前还是向后添加 if (flag) { this._events[type].unshift(cb); } else { this._events[type].push(cb); } } else { this._events[type] = [cb]; } // 监听的事件不能超过了设置的最大监听数 if (this._events[type].length === this.getMaxListeners()) { console.warn('警告-警告-警告'); } }; // 向前添加 EventEmitter.prototype.prependListener = function (type, cb) { this.on(type, cb, true); }; EventEmitter.prototype.prependOnceListener = function (type, cb) { this.once(type, cb, true); }; // 监听一次 EventEmitter.prototype.once = function (type, cb, flag) { // 先绑定,调用后删除 function wrap() { cb(...arguments); this.removeListener(type, wrap); } // 自定义属性 wrap.listen = cb; this.on(type, wrap, flag); }; // 删除监听类型 EventEmitter.prototype.removeListener = function (type, cb) { if (this._events[type]) { this._events[type] = this._events[type].filter(listener => { return cb !== listener && cb !== listener.listen; }); } }; EventEmitter.prototype.removeAllListener = function () { this._events = Object.create(null); }; // 返回所有的监听类型 EventEmitter.prototype.listeners = function (type) { return this._events[type]; }; // 发布 EventEmitter.prototype.emit = function (type, ...args) { if (this._events[type]) { this._events[type].forEach(listener => { listener.call(this, ...args); }); }};module.exports = EventEmitter;复制代码
现在可以通过引入我们自己实现的event来进行测试了
// let EventEmitter = require('events'); // 引入events模块let EventEmitter = require('./events'); // 引入events模块复制代码
That's all,如上;