Understand the Observer Pattern in Javascript

  • 2021-07-22 08:37:27
  • OfStack

Brief introduction

Observer mode is also called publish-subscribe mode (Publish/Subscribe), which defines a one-to-many relationship, so that multiple observer objects can listen to a topic object at the same time. When the state of this topic object changes, all observer objects will be notified, so that they can automatically update themselves. Reasonably, if we don't write slightly lower-level code, we may not use it. However, with it, the code will be more flexible and regular, reduce redundant code, and facilitate the development of modules and functions.

Benefits of using the Observer pattern:

Support simple broadcast communication and automatically notify all subscribed objects. After the page is loaded, it is easy for the target object to have a dynamic association with the observer, which increases the flexibility. The abstract coupling relationship between the target object and the observer can be independently extended and reused.

Introduce

In the front-end business, the place that may be used more may be custom events.
In fact, browser events are also observer mode


div.onclick = function click() {
 console.log('click')
}

Here function click subscribes to the click event of div. When we click the mouse, the event will be published and the corresponding function will be executed. This function click is an observer.

Concrete understanding

In fact, simply look at the code implementation, can also understand. But everything is connected, and these programming patterns are designed from life experience at the beginning, so concrete understanding is also a very important experience.

Let's give an example of a wedding banquet. For example, if a good friend of yours is going to get married, "getting married" doesn't happen every day, but only once or twice in a lifetime (maybe more), so our "going to his wedding" definitely doesn't happen every day, but at a specific time. I certainly can't ask him every day, 'Are you getting married today? I'll come to the banquet'. Once or twice is OK, ask every day, sb. If it is a single Wang who can't find an object, you can't kill you if you ask this every day. .

Then there needs to be an event released here, that is, 'Notify you'.

As an observer, I subscribe to the event of his "marriage", that is, we are good friends, and I will definitely go to his wedding. We have already agreed. Then I am the observer, and 'I'm going to the wedding' is the corresponding action. When I subscribe to the event of 'Marriage', I don't need to ask him every day, what should I do, pick up girls, go to dinner, watch movies, and do whatever I want.

When he announced the event of 'Marriage' and informed me, I went to do 'to attend the wedding banquet' at a specific time. function …


// Analog code 
// I subscribed 'marry'  Events 
wo.on('marry',function(){
 // Go to the wedding banquet 
})
// Then he released. For example, browser clicks 
//  Corresponding to mine  function It will be executed 

Decoupling/module/function

In fact, in the code, you need an intermediary similar to an intermediary service to manage publication and subscription.

For example, the event handler in the browser provides an interface to subscribe, and then receives the'event 'signal and publishes it to you. Let js code and browser have contact and interaction. And it was originally two different things.

In my opinion, the biggest advantage of observer mode lies in decoupling, which will make the code, sub-functions and sub-modules of one pot separated, which is clearer, lower in development cost and easier to maintain.

For example:

1. In our project, view display layer and model (data processing) logic layer start to write pages, ajax, string splicing, request to spell one interface back, and then give dom. Maybe we request an interface in an js file and an function, and are responsible for the display of view.


var xhr = new XMLHttpRequest ()
 xhr.open('get',url)
 xhr.onreadystatechange = function () {
 if(this.readyState !== 4) return
 if(this.status === 200) {
 divs.innerHTML = '<p>' + this.response + '</p>'
 //
 }
 }
 xhr.responseType = 'json'
 xhr.send(null)

In fact, the request should be separated from the display rendering.


// Request 
function getData () {
 var xhr = new XMLHttpRequest ()
 xhr.open('get',url)
 xhr.onreadystatechange = function () {
 if(this.readyState !== 4) return
 if(this.status === 200) {
 this.emit(' Render ')
 //  Publish 
 }
 }
 xhr.responseType = 'json'
 xhr.send(null)
}
// Render 
function view () {}
xhr.on(' Render ',view)

You can also do it by putting an callback directly in the status code 200. However, if I have two even rendering functions that deal with different things, do I have to change to different functions each time? Do you have to write the same request process once again?

In the words of the observer


function view1 () {}
function view2 () {}
function view3 () {}
function view4 () {}
if( I want to render view1) {
 xhr.on(' Render ',view1) // Subscribe 
 xhr.on(' Render ',view2)
}else{
 xhr.on(' Render ',view3)
 xhr.on(' Render ',view4)
}

The advantage is that with my getData function, the method is only responsible for requesting data, and then it will expose an interface for me to add methods. In this way, my getData is a relatively complete functional module. Even if I have more situations, the code in my getData will not be changed.

Sometimes we often in order to achieve business, add a new function, and change the code we wrote before, resulting in our original function module has been changed beyond recognition.

And there will be a lot of duplicate code.

Process? or Module?

Of course, it is very difficult to seal a good and complete functional module, but we must at least have a start.

Subscribe to add methods, and publish the event pool to execute.

2. MV* class framework

MVC is also a design pattern, which also applies observers.

There are also various publications and subscriptions inside it, as if it were an observer model, thus realizing a simulated in-memory dom change and calculating which DOM node should be changed. Of course, the concrete realization needs to do many things well …

3. redux

Simple implementation of an createstore function


// This is 1 Factory functions that can create store
const createStore = (reducer) => {
 let state; //  Defining the stored state
 let listeners = [];
 // getState The function of is simply to return that the current is state
 const getState = ()=> state;
 // Definition 1 Distribution function 
 // When this function is called outside, the state will be modified 
 const dispatch = (action)=>{
 // Call reducer Function modifies the status and returns 1 A new state and assign a value to this local state variable 
 state = reducer(state,action);
 // Call the listener function in turn and notify all listener functions 
 listeners.forEach(listener => listener());
 }
 // Subscribe to the function of this status, and remember to call this listener function when the status changes 
 const subscribe = function(listener){
 // Listen to this first   Add to an array 
 listeners.push(listener);
 // Return 1 The listener function is removed from the listener array when it is called 
 return function(){
  listeners = listeners.filter(l => l != listener);
 }
 }
 // Default call 1 Times dispatch To state Fu 1 Initial values 
 dispatch();
 return {
 getState,
 dispatch,
 subscribe
 }
}
let store = createStore(reducer);
// Render the data to the interface 
const render = () => {
 document.body.innerText = store.getState();
}
//  Subscribe to state change events and use the listener function when the state changes 
store.subscribe(render);
render();
var INCREASE_ACTION = {type: 'INCREMENT'};
document.addEventListener('click', function (e) {
 // Trigger 1 A Action
 store.dispatch(INCREASE_ACTION);
})

4. Role in node Most of the time we don't use EventEmitter directly, but inherit it in objects. Including fs, net, http, as long as the core modules that support event response are all subclasses of EventEmitter.

Implement a class that can publish subscriptions


'use strict'
class EmitterEvent {
 constructor() {
 // Constructor. Instance is created on the 1 Event pool 
 this._event = {}
 }
 //on  Subscribe 
 on (eventName, handler) {
 //  According to eventName The event pool has a corresponding event array, 
  Just push Add, create if not 1 A. 
 //  Rigorous 1 Point should be judged handler The type of, isn't it function
 if(this._event[eventName]) {
 this._event[eventName].push(handler)
 } else {
 this._event[eventName] = [handler]
 }
 }
 emit (eventName) {
 //  According to eventName Find the corresponding array 
 var events = this._event[eventName];
 //  Take 1 Pass in parameters to facilitate the execution of functions 
 var otherArgs = Array.prototype.slice.call(arguments,1)
 var that = this
 if(events) {
 events.forEach((event) => {
 event.apply(that, otherArgs)
 })
 }
 }
 //  Unsubscribe 
 off (eventName, handler) {
 var events = this._event[eventName]
 if(events) {
 this._event[eventName] = events.filter((event) => {
 return event !== handler
 })
 }
 }
 //  After subscribing, emit  Publish and execute 1 Automatically unsubscribe after 
 once (eventName, handler) {
 var that = this
 function func () {
 var args = Array.prototype.slice.call(arguments,0)
 handler.apply(that, args)
 this.off(eventName,func)
 }
 this.on(eventName, func)
 }
}
var event = new EmitterEvent()
function a (something) {
 console.log(something,'aa-aa')
}
function b (something) {
 console.log(something)
}
 event.once('dosomething',a)
 event.emit('dosomething', 'chifan')
 //event.emit('dosomething')
// event.on('dosomething',a)
// event.on('dosomething',b)
// event.emit('dosomething','chifan')
// event.off('dosomething',a)
// setTimeout(() => {
// event.emit('dosomething','hejiu')
// },2000)

When we need to use it, we only need to inherit this EmitterEvent class. Instances to be manipulated can use on, emit methods, that is, publish subscriptions can be used. For example, XHR, components …

Summarize

The above is the whole content of this article. I hope the content of this article can bring 1 certain help to everyone's study or work. If you have any questions, you can leave a message for communication.


Related articles: