What is the observer model?

The observer pattern, also known as publish and subscribe pattern, defines a one-to-many relationship, allowing multiple observer objects to listen to a topic object at the same time. When the state of the topic object changes, all the observing objects will be notified. It is composed of two types of objects, a topic and an observer. A topic is responsible for publishing events, while an observer observes the subject by subscribing to these events. Publishers and subscribers are completely decoupled, unaware of each other’s existence and sharing only a custom event name.

Understand the observer model:

JS traditional event is an observer mode, the reason for the observer mode is that sometimes events that are not related to traditional events, such as: direct communication problems between 2 or more modules, for example, I have an index.html page, I have many JS files, such as:

a.js: function a(){}; b.js: function b(){}; c.js function c(){}; And so on. There are more JS like this, so to initialize these functions in index.html, I need to call a() like this; b(); (c), and so on, which means the page calls from time to time I will call, increased the dependence, I need to know how many initial by invoking a function to be like this, but if we use the observer pattern now we don’t need to know what are the subscriber, such as a module (or modules) subscribed to a topic (or event), another module to release this theme, Subscribe to this topic module can perform, main let subscribers and publishers decoupled observer, the publisher does not need to know which module subscribed to this topic, it just released this topic is ok, same subscriber also don’t need to know that module will be released this topic, it just subscribe to this topic. The two modules (or more modules) are then associated. Instead of doing the code above, I need to know which modules to initialize and how TO initialize them. This is just a simple example to explain the observer pattern to use in what place, I also read many blogs about this information, but many people write blog is about how to implement the observer pattern and the benefits of the observer pattern, does not tell us when to use the observer pattern, so I cited the example above, The observer pattern can be used when multiple different business modules need to be related to each other. Just like requireJS, seaJS, and KISSY solve dependency problems (A depends on B, B depends on C, and as long as one solution entry file, the rest will be loaded asynchronously). This means that the relationships between modules can be designed using the observer pattern.

There are several implementations of this pattern, such as the jquery plugin Pub/Sub

For example:

JQuery. The subscribe (” done “, fun2);

function fun1(){

JQuery. The publish (” done “);

The jQuery above. The publish (” done “); Subscribe (” done “,fun2); subscribe(” done “,fun2);

We can also look at the NodeJS core module Events, which provides EventEmitter objects and also implements distributed Events. The following code:

var Emitter = require('events').EventEmitter; var emitter = new Emitter(); emitter.on('someEvent',function(stream){ console.log(stream + 'from eventHandler1'); }); emitter.on('someEvent',function(stream){ console.log(stream + 'from eventHandler2'); }); emitter.emit('someEvent','I am a stream! ');Copy the code

Emitters. On means emitting the event “someEvent”, and Emitters. Emit means emitting the event “someEvent”. To execute the callback function. In nodeJS we can publish a number of events called someEvent, so that each callback implements a business logic, which reduces the code coupling.

We can now implement our own Pub/Sub mode as follows:

function PubSub() { this.handlers = {}; Function (eventType,handler){var self = this; if(! (eventType in self.handlers)) { self.handlers[eventType] = []; } self.handlers[eventType].push(handler); return this; }, // emit: function(eventType){var self = this; var handlerArgs = Array.prototype.slice.call(arguments,1); for(var i = 0; i < self.handlers[eventType].length; i++) { self.handlers[eventType][i].apply(self,handlerArgs); } return self; }}; Var pubsub = new pubsub (); pubsub.on('A',function(data){ console.log(1 + data); // execute the first callback business function}); pubsub.on('A',function(data){ console.log(2 + data); // Execute the second business callback}); // Emit the event A pubsub.emit('A'," I am parameter ");Copy the code

Two: javascript custom events

Traditional Javascript events are click events, mouseover events, etc. What is a custom event? Custom events can be understood in this way that traditional events do not, just like many people invent things, what is invention? That is, the things that are not in the world are now done by themselves. This is called invention, so we can also understand the custom events in this way — at present, the traditional events do not have.

2. Why should custom events be used in places?

Traditional events can’t meet our requirements, so we need a custom event, such as traditional event click, double-click, but suddenly one day I want three strikes That is about to use a custom event, generally use custom events on the observer pattern, such as the main body need to publish messages by creating a variety of custom events, Subscribing to messages is done by registering listeners.

3. How do I create custom events?

1. Under standard browsing (except IE8 and below) we can create custom events as follows. For example:


       
I have to test
var test = document.getElementById("longen"); Var evt = document.createEvent('Event'); // Define the event type evt.initEvent('customEvent',true,true); Test.addeventlistener ('customEvent',function(){console.log("111"); },false); // Test. DispatchEvent (evT);Copy the code

The createEvent method creates an empty evT event, and initEvent is used to define the event as a custom event of the specified type. Finally a dispatchEvent is used to trigger the event. The custom event simply listens for the event and runs the callback itself. The second argument to initEvent means whether to bubble, and the third argument means whether to preventDefault(). However, the above custom events only work with standard browsers, IE8 and below. CreateEvent () is not supported, so we now need IE8 and below events. Under IE, we can use onPropertyChange event to monitor. When a property of DOM changes, the callback of onPropertyChange event will be triggered, and then we will judge whether the changed property is our custom property in the callback. If so, our callback will be executed, otherwise it will not be executed.

The following tests can be implemented in IE8 and the following code:


       
I have to test
var test = document.getElementById("longen"); document.documentElement.myEvent = 0; Function foo(){alert(' alert '); } document.documentElement.attachEvent("onpropertychange",function(event) { if (event.propertyName == "myEvent") { foo(); }}); document.documentElement.myEvent++;Copy the code

The above code can be customized in IE triggered successfully.

Synthesize: we can write a custom event across the browser, code like this:

function DefineEvent(element) { this.init(element); } DefineEvent.prototype = { constructor: DefineEvent, init: function(element) { this.element = (element && element.nodeType == 1) ? element : document; return this; }, /* * add listener event * @param {string} type listener event * @param {Function} callback Function */ addEvent: function(type,callback) { var self = this; If (self) element) addEventListener) {/ / self under the standard browser. Element. AddEventListener (type, the callback, false); }else if(self.element.attachEvent){ // IE if(isNaN(self.element[type])) { self.element[type] = 0; } var fun = function(evt){ evt = evt ? evt : window.event; if(evt.propertyName == type) { callback.call(self.element); } } self.element.attachEvent('onpropertychange',fun); // Store the binding callback on the element to remove the event binding if(! self.element['callback' + callback]) { self.element['callback' + callback] = fun; } }else { self.element.attachEvent('on' + type,callback); } return self; }, /* * removeEvent * @param {string} type listener event * @param {Function} callback Function */ removeEvent: function(type,callback){ var self = this; if(self.element.removeEventListener) { self.element.removeEventListener(type,callback,false); }else if(self.element.detachevent) {// Remove the corresponding custom property to listen self.element.detachevent ('onpropertychange',self.element['callback')  + callback]); Self. element['callback' + callback] = null; }else { self.element.detachEvent('on' + type,callback); } return self; }, /* * * triggerEvent: function(type){var self = this;}, /* * * triggerEvent: function(type){var self = this; If (self) element) dispatchEvent) {/ / / / create an Event under standard browser var evt = document. The createEvent (" Event "); // Define the event type evt.initEvent(type,true,true); / / triggers the self. The element. The dispatchEvent (evt). }else if(self.element.fireEvent) { // IE self.element[type]++; } return self; }};Copy the code

HTML

I have to test

Call as follows:

var testBox = document.getElementById('longen'); var defineEvent = new DefineEvent(testBox); Function triggerEvent(){console.log(' triggered a custom event customConsole'); Function triggerAgain(){console.log(' customConsole is triggered again '); Defineevent.addevent ('aa', triggerEvent).addevent ('aa', triggerAgain); defineEvent.triggerEvent('customConsole'); We can see on the console that two messages have been output. We can also remove a custom function, for example: defineEvent.removeEvent('aa',triggerAgain); defineEvent.triggerEvent('aa');Copy the code

I removed the triggerAgain function, and you can see that the function information is no longer there.