Publish and subscribe for common design patterns on the front end
basis
The observer pattern is the behavior pattern of an object, also known as the publish subscriber pattern, model view pattern, listener pattern, or Dependents pattern.Copy the code
Publish-subscribe | Dirty detection | The data was hijacked | The data model |
---|---|---|---|
publish-subscribe | object.observe object.defineProperty object.proxy |
Two familiar descriptors exist for objects: + Data descriptors A data descriptor is an attribute that has a value, which may or may not be writable. + Access descriptors Access descriptors are properties described by getter-setter function pairs. The descriptor must be one of these two forms; You can't be both. The property descriptor can only change if both the data descriptor and the access description are true. The default is false if enumerable is true, the property can appear in the enumerable property of the object. + value can be any valid javascript, default is undefined + writable value can be changed by the assignment operator if and only if true, default is false Access descriptors: + get provides a getter for a property, undefined if there is no getter. Method is executed with no arguments, but this object is passed undefined + set by default to provide a setter method for a property that is triggered to execute when the property value changes. This method takes a unique parameter, the new parameter value for the property.Copy the code
object.observe()
The Object.Observe API can be described as an “event handler that monitors changes to the property values of any object.” There are six kinds of changes that can be observed.
- add
- update
- delete
- reconfigure
- setPrototype
- preventExtensions
var obj = {
a: 1.b: 2
};
Object.observe(
obj,
function(changes) {
for (var change of changes) {
console.log(change); }}); obj.c =3; {
name: "c".object: obj,
type: "add"
}
obj.a = 42; {
name: "a".object: obj,
type: "update".oldValue: 1
}
delete obj.b; {
name: "b".object: obj,
type: "delete".oldValue: 2
}
Copy the code
We can also build our own listening events
var obj3 = {
_time: new Date(0)};var notifier = Object.getNotifier(obj3); // Get the Notifier object
Object.defineProperties(obj3, { // Sets the accessibility properties of the object
_time: {
enumerable: false.configrable: false
},
seen: {
set: function(val) {
var notifier = Object.getNotifier(this);
notifier.notify({
type: 'time_updated'.// Define time_updated event
name: 'seen'.oldValue: this._time
});
this._time = val;
},
get: function() {
return this._time; }}});Object.observe(obj3, function output(changes) {
changes.forEach(function(change, i) {
console.log(change, i)
});
}); // Specifies the callback function to call when the object is monitored
obj3.seen = new Date(2013.0.1.0.0.0); // Time_updated event is triggered
obj3.seen = new Date(2013.0.2.0.0.0); // Time_updated event is triggered
obj3.seen = new Date(2013.0.3.0.0.0); // Time_updated event is triggered
obj3.seen = new Date(2013.0.4.0.0.0); // Time_updated event is triggered
Copy the code
Agents can intercept actions before they occur. Object observation supports responding after a change (or group of changes) has occurred.
object.defineProperty
object.defineProperty(obj, prop, descriptor)
Obj: Object that defines attributes
Prop: Name of a property defined or modified
Descriptor: Attribute descriptor to be defined or modified
By default, attribute values added by Object.defineProperty () are not modifiable.
function Observer() {
var result = null;
Object.defineProperty(this.'result', {
get: function() {
console.log('result');
return result;
},
set: function(value) {
result = value;
console.log('You set result =' + value)
}
})
}
var app = new Observer();
app.result; // result
app.result = 11;
// Object.defineProperty is new to ES6, which is why VUE doesn't support IE8.
Copy the code
Advantages of data hijacking:
- No need to show the call. This is also the difference between Vue and React.
- Data changes can be seen. We optimized performance by hijacking the setter in defineProperty to be able to get the value of the incoming modification directly without diff comparisons.
//html<p> Please enter:</p>
<input type="text" id="input">
<p id="p"></p>
//js
window.onload=function(){
const obj = {};
Object.defineProperty(obj, 'text', {configurable:true.enumerable: true,
get(){
console.log('Get value +')
},
set(newVal){
console.log('newVal: ', newVal);
const p = document.getElementById('p');
const text = document.getElementById('input') text.value = newVal; p.innerHTML = newVal; }})const input = document.getElementById('input')
input.addEventListener('keydown'.function(e){
obj.text = e.target.value
console.log('keye: ', e.target.value); })}Copy the code
Disadvantages of data hijacking:
- We can’t define a property in advance, nor can we write one property at a time.
- The code is highly coupled.
object.proxy
Proxy is used to modify the default behavior of certain operations, which is equivalent to making changes at the language level. Therefore, it is a kind of “meta programming”, that is, programming a programming language. Here’s an example:
const me = {
name: "Xiao Ming".like: "Little red".food: "Mushroom".musicPlaying: true
};
const meWithProxy = new Proxy(me, {
get(target, prop) {
if (prop === "like") {
return "Learning";
}
return target[prop];
},
set(target, prop, value) {
if (prop === "musicPlaying"&& value ! = =true) {
console.log('value: ', target[prop], value);
throw Error("Where there is music, there is life!"); }}});console.log("proxy", meWithProxy.food);
console.log("proxy", meWithProxy.like); // If target is like, the proxy is learning
console.log("proxy", meWithProxy.musicPlaying = false); // Change to false, error.
Copy the code
Compare object.defineProperty and proxy advantages and disadvantages:
- The first defect of Object.defineProperty is the inability to listen for array changes.
- Proxies can listen directly on objects rather than properties
// Listen on objects instead of properties
const obj ={};
const p = document.getElementById('p');
const text = document.getElementById('input')
const newObj = new Proxy(obj, {
get(target, prop){
return Reflect.get(target, prop, value);
},
set(target, prop, value){
if(prop === 'text'){
text.value = value;
p.innerHTML = value;
}
return Reflect.set(target, prop, value ); }})const input = document.getElementById('input')
input.addEventListener('keyup'.function(e){
newObj.text = e.target.value
// console.log('keye: ', e.target.value);
})
Copy the code
<ul id="ul"> </ul> <button id=' BTN '> +1 </button> function (arr) { const fragement = document.createDocumentFragment(); arr.forEach(a => { var li = document.createElement('li'); li.textContent = a; fragement.appendChild(li); }) ul.appendChild(fragement); }, change(value){ var li = document.createElement('li'); li.textContent = value; const fragement = document.createDocumentFragment(); fragement.appendChild(li); ul.appendChild(fragement); }}; // initialize window.onload = function () {const BTN = document.getelementById (' BTN '); const ul = document.getElementById('ul'); // Const arr = [1, 2, 3, 4]; Render.init(arr); Const newArr = new Proxy(arr, {get: function(target, key, receiver) { return Reflect.get(target, key, receiver); }, set: function(target, key, value, receiver) { if (key ! == 'length') { Render.change(value); } return Reflect.set(target, key, value, receiver); }}); // btn.adDeventListener ('click', function () {newarr.push (newarr.length +1)}); }Copy the code
Publish and subscribe
Simple version of
function Pub(){
this.handlers = {}
}
Pub.prototype = {
// Register the binding name and callback
on(name, cb){
console.log('name: ', name);
var self = this;
if( !(name in self.handlers)){
self.handlers[name] = [];
}
self.handlers[name].push(cb)
return this
},
/ / activation
emit(name){
var self = this;
var handlerArg = Array.prototype.slice.call(arguments.1);
for(let i in self.handlers[name]){
self.handlers[name][i].apply(self, handlerArg);
}
return self;
},
remove(name, cb){
// Remove the event
console.log('name, cb: ', name, cb);
let fns = this.handlers[name]
if(! fns || ! cb){return false
}
for(let l = fns.length - 1; l >=0; l--){
let _fn = fns[l];
console.log('_fn: ', _fn);
console.log('name, cb: ', cb == _fn );
if(_fn() == cb()){
console.log('1')
fns.splice(l,1)}; }}}var pub = new Pub();
pub.on('some'.function(data){
console.log('data: ', data);
console.log('trigger some', data+1)
})
pub.on('some1'.function(data){
console.log('data1: ', data);
console.log('trigger some1', data+2)
})
pub.emit('some'.2);
pub.emit('some1'.2);
pub.remove('some'.function(data){
console.log('data: ', data);
console.log('trigger some', data+1)
})
pub.emit('some'.4);
Copy the code
The complicated version
//event.js
class Event {
The /** on method records the Event that the subscriber wants to subscribe to and the corresponding callback function in the _cbs property of the Event object */
on(event, fn) {
if (typeoffn ! ="function") {
console.error('fn must be a function')
return
}
this._cbs = this._cbs || {};
(this._cbs[event] = this._cbs[event] || []).push(fn)
}
/** The emit method takes an Event name argument, fetches the corresponding array in the _cbs property of the Event object, and executes the internal callback function */ one by one
emit(event) {
this._cbs = this._cbs || {}
var callbacks = this._cbs[event],
args
if (callbacks) {
callbacks = callbacks.slice(0)
args = [].slice.call(arguments.1)
for (var i = 0, len = callbacks.length; i < len; i++) {
callbacks[i].apply(null, args)
}
}
}
The /** off method takes the Event name and the registered callback function as arguments and removes the corresponding callback function from the _cbs property of the Event object. * /
off(event, fn) {
this._cbs = this._cbs || {}
// all
if (!arguments.length) {
this._cbs = {}
return
}
var callbacks = this._cbs[event]
if(! callbacks)return
// remove all handlers
if (arguments.length === 1) {
delete this._cbs[event]
return
}
// remove specific handler
var cb
for (var i = 0, len = callbacks.length; i < len; i++) {
cb = callbacks[i]
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1)
break}}return}}const myEvent = new Event();
export defaultmyEvent; Must export instance objects like DioCopy the code
Native to the jquery
/ / native
var btn = document.getElementById("btn");
// Create an event
var evt = document.createEvent('Event');
// Define the event type
evt.initEvent('event'.true.true);
// Listen on events
console.log('test: ', test);
btn('event'.function(){
console.log("hello world");
},false);
// Trigger the event
btn.dispatchEvent(evt);
// A custom event is nothing more than listening for the event and then running the callback itself, initEvent above
// The second argument is whether to bubble
// Whether to use preventDefault() as the third parameter
Copy the code
//jqueryJQuery. The subscribe (" done ", fun2);function fun1(){jQuery. The publish (" done "); }Copy the code
Nodejs
const EventEmitter = require('/ node - v10.16.0 / lib/events. Js');
const observer = new EventEmitter();
observer.on('topic'.function(){
console.log('topic has occured')
})
observer.once('topic'.function(msg){
console.log('message: '+ msg)
})
function main(){
console.log('start');
observer.emit('topic')
console.log('end')
}
main();
Copy the code
Afterword.
Welcome to follow the wechat public account! For better communication.Copy the code