Let’s first excerpt the text introduction from the book JavaScript Design Patterns and Development Practices //Publish and Subscribe Model in Reality
Xiao Ming recently fell in love with a house, and was told after arriving at the sales office, The houses in this development have already been sold out. Fortunately, the sales MM told Xiao Ming that there will be some late launches in the near future, and the developer is going through the relevant procedures, and you can buy them after the procedures are completed. But when exactly it will be, no one can yet know. So Xiao Ming wrote down the phone number of the sales office, and would call him every day to ask if it was time to buy. In addition to Xiao Ming, Xiao Hong, Xiao Qiang, and Xiao Long also consult the sales office on this issue every day. After a week, the salesperson decided to resign because he was tired of answering 1,000 calls with the same content every day.
Of course there is no such stupid sales company in reality. In fact, the story goes like this: Before Xiao Ming left, he left his phone number at the sales office. The sales girl promised him that he would send a message to notify Xiao Ming as soon as the new property was launched. The same goes for Xiaohong, Xiaoqiang and Xiaolong. Their phone numbers are all recorded on the roster of the sales office. When a new property is launched, the salesperson will open the roster, traverse the phone numbers on it, and send a text message one by one. to inform them. In the example above, sending SMS notifications is a typical publish-subscribe model. Buyers such as Xiao Ming and Xiao Hong are subscribers, and they have subscribed to the news that the house is for sale. As the publisher, the sales office will go through the phone numbers on the roster at the appropriate time and publish the news to the home buyers in turn.
It can be found that using the publish-subscribe model in this example has obvious advantages. Home buyers no longer need to call the sales office every day to inquire about the sale time. At the appropriate time, the sales office, as the publisher, will notify these message subscribers. There is no longer a strong coupling between home buyers and the sales office. When a new home buyer appears, he only needs to leave his mobile phone number at the sales office. The sales office does not care about any situation of the home buyer, regardless of the home buyer. Is it a man, a woman or a monkey. And any changes in the sales office will not affect the buyers. For example, the sales MM resigned, and the sales office moved from the first floor to the second floor. These changes have nothing to do with the buyers, as long as the sales office remembers to send text messages.
First, specify who will act as the publisher (such as the sales office); then add a cache list to the publisher to store callback functions to notify subscribers (sales office roster); and finally publish When sending a message, the publisher will traverse this cache list and trigger the subscriber callback functions stored in it in turn (traverse the roster and send text messages one by one). In addition, we can also fill in some parameters into the callback function, and subscribers can receive these parameters. This is very necessary. For example, the sales office can add the unit price, area, floor area ratio and other information of the house in the text message sent to the subscriber. After receiving this information, the subscriber can perform their own processing:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>观察者模式</title> </head> <body> <button id="count">点我</button> <p id="show"></p> <script> //我的个人理解 //发布者首先需要一个对象来保存事件的回调函数,这个对象的属性就是事件类型,比如{"click":[fn1,fn2],"mouseover":[fn3,fn4]} //订阅事件:首先要判断这个事件类型是否存在当前的对象上,如果不存在那就初始化一下就是添加这个属性,并且给这个属性的值设置为空数组 //如果已经存在了那么久开始把该对调函数push进去对应的属性的值, 该值为一个数组 //发布事件:发布事件就要一个个的通知订阅者,还有就是你发布了什么事件。这里要有if判断, // 先从参数中取出要发布事件的key,然后获取到key对应的回调函数数组,取出数组后先判断数组是否存在或者数组长度是否为0,如果不存在或者为0 //就结束程序,若上述条件不成立就执行for循环,一次遍历回调函数数组,并执行里面的函数,这里需要改变回调函数的this指向。防止丢之 //this。 //移出事件:先判断要移出的key的值是否为空,若空则退出程序否则再判断是否传入了要移出哪个回调函数,若空,则认为要移出全部的函数,就把数组长度设置为0 // //下面是js设计模式与开发实践上的实现过程 var Event=(function(){ var clientList={}, listen, trigger, remove; listen=function(key,fn){ if(!clientList[key]){ clientList[key]=[]; } clientList[key].push(fn); }; trigger=function(){ var key=Array.prototype.shift.call(arguments), cb=clientList[key]; if(!cb||cb.length==0){ return false;//如果没有保存回调函数就退出程序 } for(var i=0;i<cb.length;i++ ){ cb[i].apply(this,arguments);//为什么要用applly呢,这是为了把触发时传入的参数传入给cb[i]对应的回调函数里面使用并且还要改变this指向 } }; remove=function (key,fn) { var cb=clientList[key]; if(!cb){ return false;//如果要移除的key的值为空就代表没有人订阅该key和设置回调函数则直接返回 } if(!fn){//如果没有传入具体的回调函数,表示需要取消key对应的消息的所有订阅 cb&&(cb.length=0);//这里cb是已经存在了就把cb的length设置为0.我有疑问,到这里其实cb不会不存在了吧上面如果不存在就退出了如何会走到这一步? } else{ for(var l=0;l<cb.length;l++){ var _cb=cb[l]; if(_cb===fn){//寻找哪个和要删除的回调函数一样 cb.splice(l,1);//从l处删除一个元素 } } } }; return { listen:listen, trigger:trigger, remove:remove }//这里返回这个对象其实是相当于中介代理的意思,请看下面的实际例子解释: //我们给每个发布者对象都添加了listen 和trigger 方法,以及一个缓存列表clientList, //这其实是一种资源浪费。 //小明跟售楼处对象还是存在一定的耦合性,小明至少要知道售楼处对象的名字是 //才能顺利的订阅到事件。 //因此返回一个通用的对象,不必知道这个对象的名字,只需要把这个对象的一个引用给一个变量就行了 })(); Event.listen("A",fn1=function(data){ console.log(data); }) Event.listen("A",fn2=function(data){ console.log(data); }) Event.remove("A",fn1); Event.trigger("A","猴赛雷啊"); </script> <script> var a=(function(){ var count=0; var btn=document.getElementById('count'); btn.onclick=function () { Event.trigger('add',count++); } })(); var b=(function () { var p=document.getElementById('show'); Event.listen('add',function(data){ p.innerHTML=data; }) })(); </script> </body> </html>
If too many global publish-subscribe models are used to communicate between modules, Then the connection between modules is hidden behind the scenes. We will eventually not be able to figure out which module the message comes from, or which modules the message will flow to, which will bring some trouble to our maintenance. Maybe the role of a certain module is to expose some interfaces for other modules to call .