javascript Design Pattern learning: observer pattern

Keywords: Javascript network

Javascript is active in event-driven environments, such as mouse responses, event callbacks, network requests, etc. Observer mode, also known as publisher-subscriber mode, deals with the relationship between objects and their behavior and state, and manages the relationship between people and tasks.

1. The most common observer pattern

1.1 Event Listener
document.body.addEventListener('click', function () {
    console.log('you clicked me, poor guy!')
});

This is the simplest and most common observer model, besides click, there are load, blur, drag, focus, mouseover, etc. An event listener is different from an event handler. In an event listener, an event can be associated with multiple listeners, each of which handles the listened messages independently. An event handler is an association function that performs the processing of events after they occur, and an event can have a processing function:

var dom = $('.dom');
var listener1 = function(e){
    //do one thing
}
var listener2 = function(e){
    //do another thing
}
addEvent(dom,'click',listener1);
addEvent(dom,'click',listener2);

In this event listener example, listener1 and listener2 are all listeners for dom elements, and when the dom is clicked, each function is executed.

var dom = document.getElementById('dom');
var handler1 = function(e){
    //do one thing
}
var handler2 = function(e){
    //do another thing
}
dom.onclick = handler1;
dom.onclick = handler2;

In this event handler example, handler 1 is not executed, only handler 2 is executed, which is an assignment operation.

1.2 Animation

Observer mode is widely used in animation. The beginning, completion and pause of animation require the observer to determine the behavior and state of the object.

//Define animation
var Animation = function(){
    this.onStart = new Publisher;  //The design of Publisher will be introduced in Section 1.3.
    this.onComplete = new Publisher;
    this.onTween = new Publisher;
}
//Define a prototype method
Animation.prototype.look = function(){
    this.onStart.deliver('animation started!');
    this.onTween.deliver('animation is going on!');
    this.onComplete.deliver('animation completed!');  
};

//Instance of a box object
var box = new Animation();

//Define three functions as subscribers
var openBox = function(msg){
    console.log(msg)
}
var checkBox = function(msg){
    console.log(msg)
}
var closeBox = function(msg){
    console.log(msg)
}

//Subscription events
openBox.subscribe(box.onStart);
checkBox.subscribe(box.onTween);
closeBox.subscribe(box.onComplete);

//Call method
box.look()

//animation started!
//animation is going on!
//animation completed!

1.3 Construction of Observers

First, you need a publisher. Define a constructor and define an array for it to hold subscriber information:

function Publisher(){
    this.subscribes = [];
}

Publishers have the function of publishing messages, defining a deliver y prototype function:

Publisher.prototype.deliver = function(data){
    this.subscribes.forEach(function(fn){
        fn(data);
    });
    return this;
}

Next, construct the subscription method:

Function.prototype.subscribe = function(publisher){
    var that = this;
    var alreadyExists = publisher.subscribes.some(function(el){
        return el === that;
    });
    if(!alreadyExists){
        publisher.subscribes.push(this);
    }
    return this;
}

Add subscribe method directly to the prototype of Function, so that all functions can call this method. This is the end of the construction, use methods refer to the use case of 1.2 animation.
A more intuitive explanation (take onStart as an example): When the box object executes the look method, it executes onStart.deliver(), publishes the onStart event, and broadcasts the announcement'animation start!'. At this time, it has been listening to the openBox of onStart to monitor the information released by the event and print it out.

1.4 Another way to construct observers

This approach mimics the event handling mechanism of nodejs, and the code is concise:

    var scope = (function() {
    //Message List
    var events = {};
    return {
        //Subscribe
        on:function(name,hander){
            var index = 0;  //Index for recording message time
            if(events[name]){  
                //The message name already exists, and the handler is placed in the event queue of the message
                index = events[name].push(hander) - 1; 
            }else{
                events[name] = [hander];
            }
            //Returns the removal function for the current message processing event
            return function(){
                events[name].splice(index,1);
            }
        },
        //messages turning off
        off:function(name){
            if(!events[name]) return;
            //Message exists, delete message
            delete events[name];
        },
        //Message Publishing
        emit:function(name,msg){
            //Messages do not exist and are not processed
            if(!events[name]) return;
            //Message exists, and every function in the event processing queue is executed once
            events[name].forEach(function(v,i){
                v(msg);
            });
        }
    }
})();

var sayHello = scope.on('greeting',function(msg){
    console.log('Subscribe messages:' + msg);
});

var greeting = function(msg){
    console.log('Publish news:' + msg);
    scope.emit('greeting', msg);
}

greeting('hello Panfen!') 

Implementation of Observer Mode in 1.5 Noejs

There are events modules in nodejs to implement the observer mode, which can be referred to. Nodejs API-Events on the Observer Model Most of the modules are integrated with events modules, so you can use emit launch events and on listening events directly, or define them as follows.

var EventEmitter = require('events').EventEmitter;
var life = new EventEmitter();
life.setMaxListeners(11);       //Set the maximum number of listeners, default 10

//Publish and subscribe to sendName
life.on('sendName',function(name){
    console.log('say hello to '+name);
});
life.emit('sendName','jeff');

//Publish and subscribe to sendName2
function sayBeautiful(name){
    console.log(name + ' is beautiful');
}
life.on('sendName2',sayBeautiful);
life.emit('sendName2','jeff');

Common methods:

  • hasConfortListener: Used to determine whether or not the event being transmitted has a listener

  • removeListener: Remove listeners

  • listenerCount: The total number of listeners for this event

  • removeAllListeners: Remove all (or some) listeners of an event

1.6 Summary

The observer model establishes the logic of push and listen, which can be applied to situations where people's behavior and application's behavior are separated. For example, when a user clicks on a tab in the navigation bar, he opens a sub-menu with more options, and generally chooses to listen to the click event directly when he knows which element. The disadvantage of this is that the click event is directly tied to it. Better yet: create an observable onTabChange object that associates several observer implementations.

1.7 References:

Posted by Lonepig on Wed, 17 Jul 2019 15:21:15 -0700