The abstract coupling relationship between the target object and the observer can be expanded and reused independently. Body (version 1)In JS, the implementation of the observer mode is implemented through callback. We first define a pubsub object, which contains three methods: subscription, unsubscription, and release.
Var pubsub ={}; (function (q) {var topics ={}, // The subUid of the array stored by the callback function =-1; // release method q. publish = function (topic, args) {if (! Topics [topic]) {return false;} setTimeout (function () {var subscribers = topics [topic], len = subscribers? Subscribers. length: 0; while (len --) {subscribers [len]. func (topic, args) ;}}, 0); return true ;}; // subscription method q. subscribe = function (topic, func) {if (! Topics [topic]) {topics [topic] = [];} var token = (++ subUid ). toString (); topics [topic]. push ({token: token, func: func}); return token ;}; // unsubscribe method q. unsubscribe = function (token) {for (var m in topics) {if (topics [m]) {for (var I = 0, j = topics [m]. length; I <j; I ++) {if (topics [m] [I]. token = token) {topics [m]. splice (I, 1); return token ;}}} return false ;};} (pubsub ));
The usage is as follows:
// Come, subscribe to a pubsub. subscribe ('example1', function (topics, data) {console. log (topics +: + data) ;}); // publish the notification pubsub. publish ('example1', 'Hello world! '); Pubsub. publish ('example1', ['test', 'A', 'B', 'C']); pubsub. publish ('example1', [{'color': 'blue'}, {'text': 'hello'}]);
How is it? Is it easy to use? However, there is a problem with this method, that is, there is no way to unsubscribe to the subscription. To unsubscribe to the subscription, you must specify the unsubscribe name. So let's use another version:
// Assign the subscription value to a variable to unsubscribe var testsubmodules = pubsub. subscribe ('example1', function (topics, data) {console. log (topics +: + data) ;}); // publish the notification pubsub. publish ('example1', 'Hello world! '); Pubsub. publish ('example1', ['test', 'A', 'B', 'C']); pubsub. publish ('example1', [{'color': 'blue'}, {'text': 'hello'}]); // unsubscribe setTimeout (function () {pubsub. unsubscribe (testsubscribe) ;}, 0); // publish it again to verify whether the pubsub information can be output. publish ('example1', 'Hello again! (This will fail )');
Version 2We can also use the features of the prototype to implement an observer mode. The Code is as follows:
Function Observer () {this. fns = [];} Observer. prototype = {subscribe: function (fn) {this. fns. push (fn) ;}, unsubscribe: function (fn) {this. fns = this. fns. filter (function (el) {if (el! ==Fn) {return el ;}}) ;}, update: function (o, thisObj) {var scope = thisObj | window; this. fns. forEach (function (el) {el. call (scope, o) ;}};// test var o = new Observer; var f1 = function (data) {console. log ('robbin: '+ data +', hurry up! ') ;}; Var f2 = function (data) {console. log ('randall:' + data + ', ask him to increase his salary! ') ;}; O. subscribe (f1); o. subscribe (f2); o. update (Tom is back !) // Unsubscribe (f1); // verify o. update (Tom is back !);
If you cannot find the filter or forEach function, it may be because your browser is not new enough and does not support the new standard function. You can define it as follows:
if (!Array.prototype.forEach) { Array.prototype.forEach = function (fn, thisObj) { var scope = thisObj || window; for (var i = 0, j = this.length; i < j; ++i) { fn.call(scope, this[i], i, this); } };}if (!Array.prototype.filter) { Array.prototype.filter = function (fn, thisObj) { var scope = thisObj || window; var a = []; for (var i = 0, j = this.length; i < j; ++i) { if (!fn.call(scope, this[i], i, this)) { continue; } a.push(this[i]); } return a; };}
Version 3To enable multiple objects to have the observer publish and subscribe function, we can define a common function and apply the function to the object that requires the observer function. The Code is as follows:
// Common Code var observer = {// subscribe to addSubscriber: function (callback) {this. subscribers [this. subscribers. length] = callback;}, // unsubscribe removeSubscriber: function (callback) {for (var I = 0; I <this. subscribers. length; I ++) {if (this. subscribers [I] === callback) {delete (this. subscribers [I]) ;}}, // publish: function (what) {for (var I = 0; I <this. subscribers. length; I ++) {if (typeof this. subscribers [I] === 'function') {this. subscribers [I] (what) ;}}, // make: function (o) {for (var I in this) {o [I] = this [I]; o. subscribers = [] ;}};
Subscribe to the two objects blogger and user, and use the observer. make method to make the two objects have the observer function. The Code is as follows:
Var blogger = {recommend: function (id) {var msg = 'dudu recommended post: '+ id; this. publish (msg) ;}}; var user = {vote: function (id) {var msg = 'Someone voted! ID = '+ id; this. publish (msg) ;}}; observer. make (blogger); observer. make (user );
It is easy to use. subscribe to different callback functions so that they can be registered to different observer objects (or multiple observer objects can be registered at the same time ):
Var tom = {read: function (what) {console. log ('Tom saw the following information: '+ what) }}; var mm = {show: function (what) {console. log ('Mm saw the following information: '+ what) }}; // subscribe to blogger. addSubscriber (tom. read); blogger. addSubscriber (mm. show); blogger. recommend (123); // call publish // unsubscribe blogger. removeSubscriber (mm. show); blogger. recommend (456); // call publish // subscribe user of another object. addSubscriber (mm. show); user. vote (789); // call release
JQuery versionBased on the on/off function added in jQuery1.7, we can also define the observer of jQuery:
(function ($) { var o = $({}); $.subscribe = function () { o.on.apply(o, arguments); }; $.unsubscribe = function () { o.off.apply(o, arguments); }; $.publish = function () { o.trigger.apply(o, arguments); };} (jQuery));
The call method is simpler than the preceding three versions:
// The callback function handle (e, a, B, c) {// 'E' is the event object and you do not need to pay attention to the console. log (a + B + c) ;}; // subscribe $. subscribe (/some/topic, handle); // publish $. publish (/some/topic, [a, B, c]); // output abc $. unsubscribe (/some/topic, handle); // unsubscribe // subscribe $. subscribe (/some/topic, function (e, a, B, c) {console. log (a + B + c) ;}); $. publish (/some/topic, [a, B, c]); // output abc // unsubscribe (unsubscribe uses the/some/topic name instead of the callback function, different from version 1's example $. unsubscribe (/some/topic );
We can see that his subscription and Unsubscription use the string name instead of the callback function name, so even if the input is an anonymous function, we can unsubscribe it.
SummaryWhen an object needs to change other objects at the same time and does not know how many objects need to be changed, the observer mode should be considered.
In general, the work done by the observer mode is decoupling, so that both sides of the coupling depend on abstraction, rather than on specifics. So that their changes do not affect the changes on the other side.