Basic Concepts
The observer (observer) mode is widely used in client Javascript programming. All browser events are in this mode. It is also called a custom Event (custom events). Compared with events triggered by browsers, custom events represent events that are programmed by you. In addition, another alias for this mode is also called the subscriber/publisher mode.
The main motivation behind the design is to promote loose coupling. In this mode, not one object calls the method of another object, but one object subscribes to a specific activity of another object and is notified after the state changes. The subscriber is also called the observer, and the object of supplementary observation is called the publisher or topic. When an important event occurs, the publisher notifies (CALLS) all subscribers and may often send messages as event objects.
Example: magazine subscription
Suppose there is a publisher, paper, who publishes newspapers and monthly magazines every day. The subscriber joe will be notified of any news that occurs at any time.
This paper object requires a subscribers attribute, which is an array that stores all subscribers. The subscription behavior only adds it to this array. When an event occurs, paper cyclically traverses the subscriber list and notifies them. A notification means to call a method of the subscriber object. Therefore, when you subscribe to information, the subscriber must provide one of its methods to subscribe () of paper.
Paper also provides the unsubscribe () method, which means to delete a subscriber from the subscriber array (that is, the subscribers attribute. The last important method of paper is publish (), which calls the methods of these subscribers. In short, the Publisher Object paper needs to have the following members:
1. subscribers an array 2. subscribe () adds a subscriber to the subscribers array. 3. unsubscribe () deletes a subscriber from the subscribers array. 4. publish () cyclically traverses every element in the subscribers array and calls the method they provide when registering
All three methods require a type parameter, because the publisher may trigger multiple events (such as publishing a magazine and a newspaper at the same time), and the user may only choose to subscribe to one of them, instead of the other one.
Since these members are common to any publisher object, it makes sense to implement them as a part of an independent object. In this way, we can copy it to any object and convert any given object into a publisher.
The following is a general Publisher:
Var publisher = {
Subscribers :{
Any: [] // event type: subscribers
},
Subscribe: function (fn, type ){
Type = type | 'any ';
If (typeof this. subscribers [type] = "undefined "){
This. subscribers [type] = [];
}
This. subscribers [type]. push (fn );
},
Unsubscribe: function (fn, type ){
This. visitSubscribers ('unsubscribe', fn, type );
},
Publish: function (publication, type ){
This. visitSubscribers ('Publish ', publication, type );
},
VisitSubscribers: function (action, arg, type ){
Var pubtype = type | 'any ',
Subscribers = this. subscribers [pubtype],
I,
Max = subscribers. length;
For (I = 0; I <max; I + = 1 ){
If (action = 'Publish '){
Subscribers [I] (arg );
} Else {
If (subscribers [I] === arg ){
Subscribers. splice (I, 1 );
}
}
}
}
};
Here, there is a function makePublisher (), which accepts an object as an object. By copying the method of the above-mentioned universal publisher to this object, it is converted into a publisher:
Function makePublisher (o ){
Var I;
For (I in publisher ){
If (publisher. hasOwnProperty (I) & typeof publisher [I] === "function "){
O [I] = publisher [I];
}
}
O. subscribers = {any: []};
}
Now, let's implement the paper object. What it does is publish the daily report and monthly magazine:
Var paper = {
Daily: function (){
This. publish ("big news today ");
},
Monthly: function (){
This. publish ("interesting analysis", "monthly ");
}
};
Construct a paper as a publisher:
Makepublisher (paper );
As there is already a publisher, let's take a look at the subscriber object joe. There are two methods for this object:
Var joe = {
DrinkCoffee: function (paper ){
Console. log ('just read' + paper );
},
SundayPreNap: function (monthly ){
Console. log ('About to fall asleep reading this '+ monthly );
}
};
Now, paper registers joe (that is, joe subscribes to paper ):
Paper. subscribe (joe. drinkCoffee );
Paper. subscribe (joe. sundayPreNap, 'monthly ');
That is, joe provides a callable method for the default "any" event, while another callable method is used when a "monthly" event occurs. Now let's trigger some events:
Paper. daily ();
Paper. daily ();
Paper. daily ();
Paper. monthly ();
Paper. monthly ();
Paper. monthly ();
Output:
Just read big news today
Just read big news today
Just read big news today
About to fall asleep reading this interesting analysis
About to fall asleep reading this interesting analysis
About to fall asleep reading this interesting analysis
[Further expansion] Let joe become a publisher (anyone can be a publisher when using Weibo and blogs ).
MakePublisher (joe );
Joe. tweet = function (msg ){
This. publish (msg );
};
The PR department of paper decides to read the reader's tweet and subscribe to joe's information. The readTweets () method must be provided ():
Paper. readTweets = function (tweet ){
Alert ('call big meeting! Someone '+ tweet );
};
Joe. subscribe (paper. readTweets );
At this time, as long as joe sends a tweet message, paper will be notified:
Joe. tweet ("hated the paper today ");
The integrity code is as follows:
<! Doctype html>
<Html>
<Head>
<Title> Observer </title>
</Head>
<Body>
<Script>
"Use strict ";
Var publisher = {
Subscribers :{
Any: [] // event type: subscribers
},
Subscribe: function (fn, type ){
Type = type | 'any ';
If (typeof this. subscribers [type] = "undefined "){
This. subscribers [type] = [];
}
This. subscribers [type]. push (fn );
},
Unsubscribe: function (fn, type ){
This. visitSubscribers ('unsubscribe', fn, type );
},
Publish: function (publication, type ){
This. visitSubscribers ('Publish ', publication, type );
},
VisitSubscribers: function (action, arg, type ){
Var pubtype = type | 'any ',
Subscribers = this. subscribers [pubtype],
I,
Max = subscribers. length;
For (I = 0; I <max; I + = 1 ){
If (action = 'Publish '){
Subscribers [I] (arg );
} Else {
If (subscribers [I] === arg ){
Subscribers. splice (I, 1 );
}
}
}
}
};
/*
Var s1 = {log: console. log },
S2 = {err: console. error },
S3 = {warn: console. warn };
Publisher. subscribe (s1.log );
Publisher. subscribe (s2.err );
Publisher. subscribe (s3.warn );
Publisher. publish ({hello: "World "});
Publisher. unsubscribe (s2.err );
Publisher. publish ("hello ");
Publisher. subscribe (s1.log, "log ");
Publisher. publish ({obj: "log this object"}, "log ");
*/
Function makePublisher (o ){
Var I;
For (I in publisher ){
If (publisher. hasOwnProperty (I) & typeof publisher [I] === "function "){
O [I] = publisher [I];
}
}
O. subscribers = {any: []};
}
Var paper = {
Daily: function (){
This. publish ("big news today ");
},
Monthly: function (){
This. publish ("interesting analysis", "monthly ");
}
};
MakePublisher (paper );
Var joe = {
DrinkCoffee: function (paper ){
Console. log ('just read' + paper );
},
SundayPreNap: function (monthly ){
Console. log ('About to fall asleep reading this '+ monthly );
}
};
Paper. subscribe (joe. drinkCoffee );
Paper. subscribe (joe. sundayPreNap, 'monthly ');
Paper. daily ();
Paper. daily ();
Paper. daily ();
Paper. monthly ();
MakePublisher (joe );
Joe. tweet = function (msg ){
This. publish (msg );
};
Paper. readTweets = function (tweet ){
Alert ('call big meeting! Someone '+ tweet );
};
Joe. subscribe (paper. readTweets );
Joe. tweet ("hated the paper today ");
</Script>
</Body>