English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Overview
The observer pattern is also known as the publish-subscribe pattern (Publish/Subscribe), it defines a one-to-many relationship, allowing multiple observer objects to listen to a certain subject object at the same time. When the state of the subject object changes, it notifies all observer objects, enabling them to automatically update themselves. In theory, if we are not writing slightly lower-level code, we may not need it. However, it makes the code more flexible, more organized, reduces redundant code, and facilitates modular and functional development.
Benefits of using the observer pattern:
Introduction
In front-end business, the use of custom events may be more frequent.
Actually, the event handling in browsers is also based on the observer pattern.
div.onclick = function click() { console.log('click') }
Here the function click subscribes to the click event of the div. When our mouse click operation triggers the event, the corresponding function will execute. This function click is an observer.
Concretization of understanding
In fact, the pure code implementation can also be understood. However, everything is connected, and these programming patterns were also originally derived from life experiences, so a concrete understanding is also very important.
Let's take the example of a wedding banquet. For example, if one of your good friends is getting married, the event of 'getting married' does not happen every day, it only happens once or twice in a lifetime, so our 'attending his wedding' definitely does not happen every day, but only at specific times. I can't ask him every day, 'Are you getting married today, and I will come to the banquet'. Once or twice is okay, but asking every day is silly. Suppose you are a single person who can't find a partner, and you are asked like this every day, you might kill you...
So here there needs to be an event published, that is, 'notify you'.
As an observer, I subscribe to his 'marriage' event, which means we are good friends, and I will definitely attend his wedding because we have already agreed. So I am the observer, and 'I attend the wedding' is the corresponding action. When I subscribe to the 'marriage' event, I don't need to ask him every day, I can do whatever I want, go out to meet girls, have dinner, watch movies, and so on.
When he publishes the 'marriage' event and notifies me, I will go to the specific behavior function 'attend the wedding banquet' at the right time...
//Mock code //I subscribed to the 'marry' event wo.on('marry',function(){ //To attend the wedding banquet } //And then he publishes. For example, the browser's click event // Then my function will execute
Decoupling/Module/Functionality
In fact, there is a need for a similar intermediate service in the code, a middleman to manage publishing and subscribing.
For example, the event handler in the browser provides an interface for subscription and then receives 'event' signals to publish to you. This makes the JavaScript code interact with the browser, creating a connection between the two originally different things.
In my opinion, the biggest benefit of the observer pattern is decoupling, which allows us to separate the code into functions and modules, making it clearer, reducing development costs, and easier to maintain.
For example:
1In our project, the view presentation layer and the model (data processing) logic layer are initially written for the page, AJAX, string concatenation, and then a request is made to an interface to combine it with the DOM. It's possible that within one JS file and one function, there are requests to interfaces and also the responsibility for the view's display.
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, it should be separated into requesting and displaying/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) } //Rendering function view () {} xhr.on('render', view)
Directly in the status code2I can also do it by placing a callback at 00. But, if I have two or more rendering functions that handle different things, do I have to change the function each time? Isn't the same request process still to be written?
In terms of observer
function view1 () {} function view2 () {} function view3 () {} function view4 () {} if(I want to render view1) { xhr.on('render', view1) //Subscribe xhr.on('render', view2) } xhr.on('render', view3) xhr.on('render', view4) }
The benefit lies in my getData function, which is only responsible for requesting data, and then it will expose an interface for me to add methods. So my getData is relatively a complete functional module, and even if I have more situations, the code inside my getData will not change.
Sometimes we often change the code we have written before to add a new feature for business implementation, which leads to the original functional module being changed beyond recognition.
And there will be a lot of repetitive code.
Process? or Module?
Of course, it's quite difficult to encapsulate a good and complete functional module, but we at least need to have a starting point.
Subscribe to add methods, and the event pool is executed when published.
2, MV* Class framework
MVC is also a design pattern, and observers are also applied here.
It also internally uses various publish-subscribe mechanisms, as if it is an observer model, thus achieving a simulated in-memory DOM change and calculating which DOM node should change. Of course, the specific implementation involves many things... I won't go into detail...
3, redux
A simple implementation of a createStore function
//This is a factory function that can create a store const createStore = (reducer) => { let state; // Define the stored state let listeners = []; // The role of getState is very simple, which is to return the current state const getState = ()=> state; //Define a dispatch function //When this function is called from the outside, it will modify the state const dispatch = (action)=>{ //Call the reducer function to modify the state, return a new state and assign it to this local state variable state = reducer(state,action); //Call the listener functions in turn, notifying all listener functions listeners.forEach(listener => listener()); } //Subscribe to this state function, and remember to call this listener function when the state changes const subscribe = function(listener){ //First add this listener to the array listeners.push(listener); //Return a function that, when called, removes this listener function from the listener array return function(){ listeners = listeners.filter(l => l != listener); } } //The default dispatch calls once to assign an initial value to the state dispatch(); return { getState, dispatch, subscribe } } let store = createStore(reducer); //Render 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 an Action store.dispatch(INCREASE_ACTION); }
4The role of EventEmitter in node.js: Most of the time, we do not use EventEmitter directly, but inherit it in objects. Core modules that support event response, including fs, net, and http, are all subclasses of EventEmitter.
Implement a class that can publish and subscribe
'use strict' class EmitterEvent { constructor() { //Constructor. Create an event pool on the instance this._event = {} } //Subscription on(eventName, handler) { // According to eventName, there is a corresponding event array in the event pool, Add it if it exists, or create a new one if it doesn't. // It should be more rigorous to judge the type of handler, whether it is a function this._event[eventName].push(handler) else { } this._event[eventName] = [handler] } } emit(eventName) { // Find the corresponding array according to eventName var events = this._event[eventName]; // Take the parameters passed in to facilitate the execution of the function 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 subscription, the subscription is automatically canceled after the emit is executed once 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 just need to inherit the EmitterEvent class. The instance to be operated can be used with the on, emit method, that is, it can be used with publishing and subscribing. For example, XHR, component...
Summary
That's all for this article. I hope the content of this article can bring some help to everyone's learning or work. If you have any questions, you can leave a message for communication.
Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal responsibility. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (When reporting, please replace # with @) for complaints, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.