Introduced
The observer pattern, also known as the Publish subscription model (Publish/subscribe), defines a one-to-many relationship in which multiple observer objects listen to a Subject object at the same time, and the subject object's state changes to notify all observer objects so that they can automatically update themselves.
Benefits of using the Observer pattern:
1. Support Simple broadcast communication and automatically notify all objects that have been subscribed to.
2. The target object can easily be dynamically associated with the viewer after the page is loaded, adding flexibility.
3. The abstract coupling relationship between the target object and the observer can be extended and reused separately.
Body (version i)
JS in the implementation of the observer model is implemented through callbacks, we first define a PubSub object, which contains 3 methods: subscription, unsubscribe, publish.
Copy Code code as follows:
var pubsub = {};
(function (q) {
var topics = {},//callback function stored array
Subuid =-1;
Publish 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;
};
How to subscribe
Q.subscribe = function (topic, func) {
if (!topics[topic]) {
Topics[topic] = [];
}
var token = (++subuid). toString ();
Topics[topic].push ({
Token:token,
Func:func
});
return token;
};
How to Unsubscribe
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 use of the following methods:
Copy Code code as follows:
Here, subscribe to a
Pubsub.subscribe (' example1 ', function (topics, data) {
Console.log (Topics + ":" + data);
});
Post notification
Pubsub.publish (' example1 ', ' Hello world! ');
Pubsub.publish (' example1 ', [' Test ', ' a ', ' B ', ' C ']);
Pubsub.publish (' example1 ', [{' Color ': ' Blue '}, {' text ': ' Hello '}]);
What do you think? Isn't it cool to use it? But there is a problem in this way, that is, there is no way to unsubscribe to unsubscribe, you must specify the name of the unsubscribe, so we have another version:
Copy Code code as follows:
Assign a subscription to a variable in order to unsubscribe
var testsubscription = pubsub.subscribe (' example1 ', function (topics, data) {
Console.log (Topics + ":" + data);
});
Post 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 (testsubscription);
}, 0);
Once again, verify that you are able to output information
Pubsub.publish (' example1 ', ' Hello again! (This would fail) ');
Version Two
We can also use the characteristics of the prototype to implement an observer pattern, the code is as follows:
Copy Code code 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 to work! ');
};
var F2 = function (data) {
Console.log (' Randall: ' + data + '), ask him to add some pay! ');
};
O.subscribe (F1);
O.subscribe (F2);
O.update ("Tom's Back!") ")
Unsubscribe F1
O.unsubscribe (F1);
again to verify
O.update ("Tom's Back!") ");
If you are prompted not to find the filter or foreach function, perhaps because your browser is not new enough to temporarily support new standard functions, you can define yourself by using the following methods:
Copy Code code 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 Three
If you want multiple objects to have an observer publish subscription function, we can define a generic function and then apply the function to the object that needs the Observer function, as follows:
Copy Code code as follows:
Common code
var observer = {
Subscription
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]);
}
}
},
Release
Publish:function (what) {
for (var i = 0; i < this.subscribers.length; i++) {
if (typeof this.subscribers[i] = = ' function ') {
This.subscribers[i] (what);
}
}
},
The object o has observer capabilities
Make:function (o) {
for (var I and this) {
O[i] = this[i];
O.subscribers = [];
}
}
};
Then subscribe to 2 objects Blogger and user, using the Observer.make method to the 2 objects with the Observer function, the code is as follows:
Copy Code code as follows:
var blogger = {
Recommend:function (ID) {
var msg = ' Dudu Recommended posts: ' + ID;
This.publish (msg);
}
};
var user = {
Vote:function (ID) {
var msg = ' Someone voted for!id= ' + ID;
This.publish (msg);
}
};
Observer.make (blogger);
Observer.make (user);
Using the method is simpler, subscribing to different callback functions so that you can register with different observer objects (or you can register in multiple observer objects at the same time):
Copy Code code as follows:
var Tom = {
Read:function (what) {
Console.log (' Tom saw the following message: ' + What ')
}
};
var mm = {
Show:function (what) {
Console.log (' mm saw the following message: ' + What ')
}
};
Subscription
Blogger.addsubscriber (Tom.read);
Blogger.addsubscriber (mm.show);
Blogger.recommend (123); Call Publishing
Unsubscribe
Blogger.removesubscriber (mm.show);
Blogger.recommend (456); Call Publishing
A subscription to another object
User.addsubscriber (mm.show);
User.vote (789); Call Publishing
jquery version
Based on the new On/off features of the jQuery1.7 version, we can also define the jquery version of the Observer:
Copy Code code as follows:
(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 invocation method is simpler than the previous 3 versions:
Copy Code code as follows:
callback function
function handle (E, A, B, c) {
' E ' is an event object that needs no attention
Console.log (A + B + c);
};
Subscription
$.subscribe ("/some/topic", handle);
Release
$.publish ("/some/topic", ["a", "B", "C"]); Output ABC
$.unsubscribe ("/some/topic", handle); Unsubscribe
Subscription
$.subscribe ("/some/topic", function (E, a, B, c) {
Console.log (A + B + c);
});
$.publish ("/some/topic", ["a", "B", "C"]); Output ABC
Unsubscribe (unsubscribe using the/some/topic name, not the callback function Oh, and version one of the example is not the same
$.unsubscribe ("/some/topic");
As you can see, his subscriptions and unsubscribe use a string name, not a callback function name, so we can unsubscribe even if the anonymous function is passed in.
Summarize
The observer's use of the situation is: when an object changes need to change the other objects at the same time, and it does not know how many objects need to change, you should consider the use of observer mode.
In general, the work of the observer pattern is decoupling, so that both sides of the coupling are dependent on abstraction rather than on concrete. So that their own changes will not affect the other side of the change.