Preface
This series of articles is mainly based on "JavaScript Design Patterns and Development Practices", which will add some of their own thinking. I hope it will be helpful to you all.
concept
The proxy mode is to provide a substitute or placeholder for an object in order to control its access.
UML class diagram
scene
For example, stars are represented by brokers. If you want to invite a star to a commercial show, you can only contact his agent. The broker will negotiate the details and remuneration of the commercial performance before handing over the contract to the star for signature.
classification
Protection agent
To control access to target objects by objects with different privileges, such as the above example of star brokers
Virtual agent
Delay the creation of expensive objects until they are really needed. If many http requests are initiated in a short time, we can use virtual proxy to send requests uniformly in a certain time.
Advantages and disadvantages
Advantage
1. Objects can be protected 2. Optimize performance and reduce overhead objects 3. Caching results
Example
Picture preloading
Load a picture
var myImage = (function () { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc: function (src) { imgNode.src = src; } } })(); myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
Imagine if we had a big picture, users would see that the page was blank for a long time.
The improvement we can think of is to show loading pictures before they are loaded.
Add a loading picture
var myImage = (function () { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); var img = new Image() img.onload = () => { // Analog Picture Loading setTimeout(() => { imgNode.src = img.src }, 1000) } return { setSrc: function (src) { img.src = src imgNode.src = 'https://content.igola.com/static/WEB/images/other/loading-searching.gif'; } } })(); myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
This code violates the single responsibility principle. This object is responsible for loading and preloading images at the same time.
At the same time, it violates the principle of open and closed. If we don't need to pre-load pictures in the future, we have to modify the whole object.
Improvement with Virtual Agent
var myImage = (function () { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc: function (src) { imgNode.src = src } } })(); var proxyImage = (function() { var img = new Image() img.onload = function() { myImage.setSrc(img.src) } return { setSrc: function (src) { img.src = src myImage.setSrc('https://content.igola.com/static/WEB/images/other/loading-searching.gif') } } })() proxyImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
Note: Our proxy and ontology interfaces should be consistent. For example, both proxy Image and myImage above return an object containing setSrc methods. In this regard, we also have some traces to follow when we write agents.
Virtual proxy merges HTTP requests
Simple implementation
<body> <div id="wrapper"> <input type="checkbox" id="1"></input>1 <input type="checkbox" id="2"></input>2 <input type="checkbox" id="3"></input>3 <input type="checkbox" id="4"></input>4 <input type="checkbox" id="5"></input>5 <input type="checkbox" id="6"></input>6 <input type="checkbox" id="7"></input>7 <input type="checkbox" id="8"></input>8 <input type="checkbox" id="9"></input>9 </div> </body> <script type="text/javascript"> // Simulate http requests var synchronousFile = function (id) { console.log('Start synchronizing files. id by: ' + id); }; var inputs = document.getElementsByTagName('input') var wrapper = document.getElementById('wrapper') wrapper.onclick = function (e) { if (e.target.tagName === 'INPUT') { synchronousFile(e.target.id) } } </script>
The drawback is obvious: an http request is sent once at every point
Improvement
<body> <div id="wrapper"> <input type="checkbox" id="1"></input>1 <input type="checkbox" id="2"></input>2 <input type="checkbox" id="3"></input>3 <input type="checkbox" id="4"></input>4 <input type="checkbox" id="5"></input>5 <input type="checkbox" id="6"></input>6 <input type="checkbox" id="7"></input>7 <input type="checkbox" id="8"></input>8 <input type="checkbox" id="9"></input>9 </div> </body> <script type="text/javascript"> // Simulate http requests var synchronousFile = function (id) { console.log('Start synchronizing files. id by: ' + id); }; var inputs = document.getElementsByTagName('input') var wrapper = document.getElementById('wrapper') wrapper.onclick = function (e) { if (e.target.tagName === 'INPUT' && e.target.checked) { proxySynchronousFile(e.target.id) } } var proxySynchronousFile = (function () { var cacheIds = [], timeId = 0 return function (id) { if (cacheIds.indexOf(id) < 0) { cacheIds.push(id) } clearTimeout(timeId) timeId = setTimeout(() => { synchronousFile(cacheIds.join(',')) cacheIds = [] }, 1000) } })() </script>
Cache Proxy-Computational Product
Rough Realization
var mult = function () { console.log('Start calculating the product'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; mult(2, 3); // Output: 6 mult(2, 3, 4); // Output: 24
Improvement
var mult = function () { console.log('Start calculating the product'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; // mult(2, 3); / / Output: 6 // mult(2, 3, 4); / / Output: 24 var proxyMult = (function() { var cache = {} return function () { let id = Array.prototype.join.call(arguments, ',') if (cache[id]) { return cache[id] } else { return cache[id] = mult.apply(this, arguments) } } })() proxyMult(2, 3); // Output: 6 proxyMult(2, 3); // Output: 6
We now hope that additions can also be cached
Further improvement
var mult = function () { console.log('Start calculating the product'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; var plus = function () { console.log('Start calculating and'); var a = 0; for (var i = 0, l = arguments.length; i < l; i++) { a = a + arguments[i]; } return a; }; // mult(2, 3); / / Output: 6 // mult(2, 3, 4); / / Output: 24 var createProxyFactory = function (fn) { var cache = {} return function () { let id = Array.prototype.join.call(arguments, ',') if (cache[id]) { return cache[id] } else { return cache[id] = fn.apply(this, arguments) } } } var proxyMult = createProxyFactory(mult), proxyPlus = createProxyFactory(plus); proxyMult(1, 2, 3, 4) // Output: 24 proxyMult(1, 2, 3, 4) // Output: 24 proxyPlus(1, 2, 3, 4) // Output: 10 proxyPlus(1, 2, 3, 4) // Output: 10
Agent mode of es6
Class-based Implementation
class Car { drive() { return "driving"; }; } class CarProxy { constructor(driver) { this.driver = driver; } drive() { return ( this.driver.age < 18) ? "too young to drive" : new Car().drive(); }; } class Driver { constructor(age) { this.age = age; } }
Implementation Based on Proxy
// Star let star = { name: 'Zhang XX', age: 25, phone: '13910733521' } // Agent let agent = new Proxy(star, { get: function (target, key) { if (key === 'phone') { // Return to the broker's cell phone number return '18611112222' } if (key === 'price') { // Stars do not quote, brokers quote return 120000 } return target[key] }, set: function (target, key, val) { if (key === 'customPrice') { if (val < 100000) { // Minimum 10w throw new Error('The price is too low.') } else { target[key] = val return true } } } }) // Sponsor? console.log(agent.name) console.log(agent.age) console.log(agent.phone) console.log(agent.price) // Want to offer your own quotation (bargaining, or scrambling for high prices) agent.customPrice = 150000 // agent.customPrice = 90000 // / Error Reporting: Price is too low console.log('customPrice', agent.customPrice)